ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

AutoGPT Platform 后端测试实战:pytest、快照测试与 FastAPI 路由鉴权 Mock 指南

AutoGPT Platform 后端测试实战:pytest、快照测试与 FastAPI 路由鉴权 Mock 指南 AutoGPT Platform 后端测试实战pytest、快照测试与 FastAPI 路由鉴权 Mock 指南【免费下载链接】AutoGPTAutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.项目地址: https://gitcode.com/GitHub_Trending/au/AutoGPT本文基于 AutoGPT Platform 后端官方测试指南 TESTING.md 整理完整覆盖其测试运行方式、pytest-snapshot 快照测试原理、FastAPI 路由测试写法与 JWT 鉴权 Mock 策略并结合当前仓库中的 pyproject.toml、run_tests.py、两层 conftest.py 与真实快照文件把每一条命令、每一个 fixture 落实到可验证的源码证据上。读完本文你可以直接在本地跑起 Platform 后端完整测试套件、理解其独立测试数据库的隔离机制并掌握为新增 API 路由编写带鉴权 Mock 与快照断言的测试的完整套路。测试技术栈与依赖配置官方指南明确 Platform 后端以pytest为核心测试框架配套四个关键库pytest— 测试框架本体pytest-asyncio— 异步测试支持pytest-mock— Mock 支持提供mockerfixturepytest-snapshot— 针对 API 响应的快照测试。这些依赖在 pyproject.toml 中可以直接核对pytest ^8.4.1、pytest-asyncio ^1.1.0、pytest-snapshot ^0.9.0声明在[tool.poetry.dependencies]中而pytest-mock ^3.15.1、pytest-cov ^7.1.0位于 dev 依赖组[tool.poetry.group.dev.dependencies]。此外仓库还引入了poethepoet作为任务运行器并通过[tool.poetry.scripts]把test命令注册为指向scripts.run_tests:test的可执行入口——这正是下一节poetry run test背后的实现。pytest 全局配置pyproject.toml 的[tool.pytest.ini_options]中还包含若干对测试行为有实质影响的全局配置值得在写测试前了解[tool.pytest.ini_options] asyncio_mode auto asyncio_default_fixture_loop_scope session # 禁用 syrupy 插件避免与 pytest-snapshot 冲突 # 两者都提供 --snapshot-update 参数会引发 ArgumentError addopts -p no:syrupy # 单个测试超过 5 分钟时 dump 所有线程栈不中断运行让挂起自我暴露 faulthandler_timeout 300 markers [ supplementary: tests kept for coverage but superseded by integration tests, integration: end-to-end tests that require a live database (skipped in CI), slow: tests that take more than a few seconds (e.g. spin up an isolated docker stack), ]几个要点asyncio_mode auto意味着async def测试无需手动加pytest.mark.asyncio即可被识别但官方指南仍建议直接测试异步函数时显式使用该标记-p no:syrupy是硬性要求syrupy 与 pytest-snapshot 都注册了--snapshot-update参数共存会直接导致 pytest 启动失败faulthandler_timeout 300是一道防挂起保险配置注释中记录了真实案例——某单元测试触发了一个永不返回的 DatabaseManager 套接字读最终靠线程栈 dump 定位三个 markersupplementary/integration/slow用于区分测试性质其中integration标记的测试需要真实数据库在 CI 中会被跳过。运行测试命令与底层流程常用命令官方指南给出的日常命令如下均在autogpt_platform/backend目录下执行# 运行全部测试 poetry run test # 运行指定测试文件 poetry run pytest path/to/test_file.py # 详细输出 poetry run pytest -v # 带覆盖率 poetry run pytest --covbackendpoetry run test到底做了什么poetry run test并非直接调用 pytest而是执行 scripts/run_tests.py 中的test()函数。阅读源码可以看到完整的编排流程拉起测试基础设施执行docker compose -f docker-compose.test.yaml --env-file ../.env up -d。docker-compose.test.yaml 定义了四类服务dbPostgreSQL复用 db/docker 中 Supabase 的 db 定义并直接暴露 5432 端口、redis、rabbitmq和clamav为需要消息队列与文件扫描的测试提供本地依赖。等待数据库就绪wait_for_postgres()循环调用容器内pg_isready最多重试 5 次、每次间隔 5 秒。强制切换到独立测试库关键隔离设计源码注释明确指出这是load-bearing步骤——测试库容器与开发用 Supabase 数据库共享数据目录而 Supabase 的默认search_path是$user, platform, public如果直接用无 schema 限定连接串prisma migrate reset --force会命中线上的platformschema。因此脚本会先执行CREATE DATABASE agpt_test再显式设置环境变量test_db_name agpt_test test_env[DATABASE_URL] fpostgresql://{db_user}:{db_pass}localhost:{db_port}/{test_db_name} test_env[DIRECT_URL] test_env[DATABASE_URL]这样prisma migrate reset --force --skip-seed与后续prisma migrate deploy都只作用于独立的agpt_test数据库开发数据不受影响。执行测试subprocess.run([pytest] sys.argv[1:], envtest_env)—— 注意sys.argv[1:]会把额外参数透传给 pytest因此poetry run test path/to/test.py这类用法是可行的。清理无论测试成败最后执行docker compose -f docker-compose.test.yaml down拆掉容器并以 pytest 的返回码作为整个命令的退出码。手动等价流程如果想理解每一步也可以手动复现cd autogpt_platform/backend docker compose -f docker-compose.test.yaml --env-file ../.env up -d # 创建独立测试库 docker compose -f docker-compose.test.yaml exec -T db psql -U postgres -d postgres \ -c CREATE DATABASE agpt_test # 设置 DATABASE_URL / DIRECT_URL 指向 agpt_test 后执行迁移 prisma migrate reset --force --skip-seed prisma migrate deploy # 运行测试 pytest docker compose -f docker-compose.test.yaml down快照测试原理、更新与最佳实践工作原理官方指南对快照测试的描述是首次运行时在snapshots/目录创建快照文件后续运行将输出与已保存快照比对一旦检测到差异测试即失败。仓库中真实存在这一层结构——autogpt_platform/backend/snapshots/目录提交了 80 余个快照文件如agts_search、grph_struct、otto_ok、admin_add_credits_success等它们随代码一起进入版本库是 CI 比对的基础。例如 snapshots/otto_ok 的内容就是 Otto 端点一次成功响应的规范化 JSON{ answer: This is Ottos response to your query., documents: [ { relevance_score: 0.95, url: https://example.com/doc1 }, { relevance_score: 0.87, url: https://example.com/doc2 } ], success: true }从结构看该文件正是json.dumps(..., indent2, sort_keysTrue)的产物键已排序、缩进为 2diff 可读性好。创建与更新快照首次编写测试、或预期输出发生合法变化时使用--snapshot-update参数重建快照poetry run pytest path/to/test.py --snapshot-update官方指南在此处有明确警示提交前务必审查快照变更用git diff确认变化是预期的——快照文件是黄金基线误更新会掩盖真实回归。快照测试示例指南给出的标准写法如下import json from pytest_snapshot.plugin import Snapshot def test_api_endpoint(snapshot: Snapshot): response client.get(/api/endpoint) # Snapshot the response snapshot.snapshot_dir snapshots snapshot.assert_match( json.dumps(response.json(), indent2, sort_keysTrue), endpoint_response )快照最佳实践官方指南归纳了四条规则每一条都对应快照稳定性问题使用描述性名称user_list_response而非response1排序 JSON 键sort_keysTrue保证字典顺序不影响比对结果格式化 JSONindent2让 diff 更易读剔除动态数据移除时间戳、ID 等每次运行都会变化的字段。针对第 4 点指南给出了剔除动态字段的完整示例response_data response.json() # Remove dynamic fields for snapshot response_data.pop(created_at, None) response_data.pop(id, None) snapshot.snapshot_dir snapshots snapshot.assert_match( json.dumps(response_data, indent2, sort_keysTrue), static_response_data )为 API 路由编写测试基本结构Platform 后端的 API 路由测试遵循独立 FastAPI 实例 TestClient的轻量模式只挂载被测 router不启动完整应用。指南中的标准骨架import json import fastapi import fastapi.testclient import pytest from pytest_snapshot.plugin import Snapshot from backend.api.features.myroute import router app fastapi.FastAPI() app.include_router(router) client fastapi.testclient.TestClient(app) def test_endpoint_success(snapshot: Snapshot): response client.get(/endpoint) assert response.status_code 200 # Test specific fields data response.json() assert data[status] success # Snapshot the full response snapshot.snapshot_dir snapshots snapshot.assert_match( json.dumps(data, indent2, sort_keysTrue), endpoint_success_response )这种模式在真实测试中大量使用。例如 backend/api/features/home/routes_test.py 就是标准写法模块顶部app fastapi.FastAPI(); app.include_router(router); client fastapi.testclient.TestClient(app)测试函数中同时使用精确断言校验特定字段与快照断言校验整体响应形态。鉴权测试Mockget_jwt_payload这是指南中最具实战价值的一节。主 API 路由的 JWT 鉴权由autogpt_libs.auth模块提供官方推荐的做法是mock 最底层的get_jwt_payload函数——所有高层鉴权函数requires_user、requires_admin_user、get_user_id都建立在它之上mock 一处即可覆盖全部。反之若测试并不真正消费user_id则可以不做任何 mock测试环境下鉴权本来就是关闭的见 conftest.py 的会话级 fixture 机制。全局鉴权 fixturesbackend/api/conftest.py仓库内实际路径为 backend/api/conftest.py提供两个全局鉴权 fixturemock_jwt_user—— 普通用户mock_jwt_admin—— 管理员用户。用法是通过 FastAPI 的dependency_overrides把 mock payload 注入到被测 appimport fastapi import fastapi.testclient import pytest from backend.api.features.myroute import router app fastapi.FastAPI() app.include_router(router) client fastapi.testclient.TestClient(app) pytest.fixture(autouseTrue) def setup_app_auth(mock_jwt_user): Setup auth overrides for all tests in this module from autogpt_libs.auth.jwt_utils import get_jwt_payload app.dependency_overrides[get_jwt_payload] mock_jwt_user[get_jwt_payload] yield app.dependency_overrides.clear()管理员端点则换成mock_jwt_adminpytest.fixture(autouseTrue) def setup_app_auth(mock_jwt_admin): Setup auth overrides for admin tests from autogpt_libs.auth.jwt_utils import get_jwt_payload app.dependency_overrides[get_jwt_payload] mock_jwt_admin[get_jwt_payload] yield app.dependency_overrides.clear()结合当前源码可以补充几个细节让上述模式更完整fixture 的真实返回内容。backend/api/conftest.py 中mock_jwt_user返回{sub: test_user_id, role: user, email: testexample.com}mock_jwt_admin返回role: admin、email: test-adminexample.com。需要说明的是指南中把 ID 描述为test-user-id/admin-user-id而从源码看这些 fixture 底层依赖test_user_id/admin_user_id其真实取值是固定的 UUID定义在 backend/conftest.py 中例如test_user_id为3e53486c-cf57-477e-ba2a-cb02dc828e1a指南中的字符串可视为示意值。组织/团队上下文也需一并 override。当前版本新增了RequestContext组织、团队、席位状态mock fixture 同时提供get_request_context。以 home/routes_test.py 为例生产中的标准写法同时覆盖两个依赖pytest.fixture(autouseTrue) def setup_app_auth(mock_jwt_user): from autogpt_libs.auth import get_request_context from autogpt_libs.auth.jwt_utils import get_jwt_payload app.dependency_overrides[get_jwt_payload] mock_jwt_user[get_jwt_payload] app.dependency_overrides[get_request_context] mock_jwt_user[get_request_context] yield app.dependency_overrides.clear()独立 ID fixtures。test_user_id、admin_user_id、target_user_id用于管理员对普通用户的操作场景均可单独注入此外 backend/conftest.py 还提供setup_test_user/setup_admin_user异步 fixture在需要真实数据库用户记录如subemailuser_metadata时使用并内置了对Event loop is closed瞬态错误的重试逻辑。支付墙自动旁路。从源码结构看backend/api/conftest.py中还有一个autouse的_bypass_paywallfixture它把backend.copilot.rate_limit.is_user_paywalled与backend.executor.utils.is_user_paywalled都 Mock 为返回False因为测试环境没有真实 Supabase 用户行支撑 JWT若不旁路所有付费墙路由会因 tier 查询失败而 503。专门测试付费墙行为的用例则直接 patch_fetch_user_tier绕开该旁路。快照配置复用configured_snapshot指南提到backend/api/conftest.py还提供configured_snapshotfixture。从源码看它只是预置了snapshot.snapshot_dir snapshots再返回原生snapshotpytest.fixture def configured_snapshot(snapshot: Snapshot) - Snapshot: Pre-configured snapshot fixture with standard settings. snapshot.snapshot_dir snapshots return snapshot使用它可省去每次手动设置snapshot_dir的样板代码。Mock 外部服务对于依赖第三方 API 的路由指南推荐用pytest-mock的mockerfixture 打补丁再对响应做快照def test_external_api_call(mocker, snapshot): # Mock external service mock_response {external: data} mocker.patch( backend.services.external_api.call, return_valuemock_response ) response client.post(/api/process) assert response.status_code 200 snapshot.snapshot_dir snapshots snapshot.assert_match( json.dumps(response.json(), indent2, sort_keysTrue), process_with_external_response )异步外部调用则遵循指南Troubleshooting一节的建议使用AsyncMock对异步函数打补丁FastAPI TestClient 会自行处理事件循环路由测试用普通def即可。测试组织、隔离与最佳实践测试组织规范官方指南的组织原则测试与代码同置routes.py→routes_test.py当前仓库大量测试文件遵循该命名如backend/api/features/store/routes_test.py描述性测试名如test_create_user_with_invalid_email相关测试按需用 class 分组覆盖维度happy path 与错误路径都要测边界情况空数据、非法格式要测鉴权与授权要测。测试隔离指南要求所有测试通过 fixture 保证隔离具体到源码层面鉴权 override 自动清理setup_app_authfixture 在yield之后调用app.dependency_overrides.clear()确保不串到下一个测试数据库连接妥善管理根级 conftest.py 提供server会话级 fixture基于backend.util.test.SpinTestServer并有 autouse 的graph_cleanupfixture——它包装test_create_graph记录所有测试创建的 graph测试结束后逐个删除并断言确实被删掉防止测试数据在agpt_test库中累积Mock 对象自动重置mockerfixture 为函数级作用域patch 随测试结束自动还原。自定义 fixture指南同时鼓励把常见测试数据抽成 fixture例如pytest.fixture def sample_user(): return { email: testexample.com, name: Test User } def test_create_user(sample_user, snapshot): response client.post(/users, jsonsample_user) # ... test implementation异步测试准则FastAPI TestClient 测试用普通def客户端内部处理异步直接测试异步函数用async defpytest.mark.asyncio由于asyncio_mode autoasync def测试开箱即跑但显式标记仍有助于可读性。CI/CD 集成与按需触发自动触发与路径过滤官方指南说明GitHub Actions 工作流会在 pull request 与 main 分支 push 时自动运行测试快照测试在 CI 中依赖三个前提快照文件已提交入库、CI 与已提交快照比对、不一致即失败——这与仓库中snapshots/目录被整体纳入版本管理的事实一致。指南还特别指出自动触发是路径过滤的只有当变更触及autogpt_platform/backend/**、autogpt_platform/autogpt_libs/**、工作流文件本身或 lockfile 脚本时才会触发。只改前端/文档的分支永远不会跑后端 CI。手动触发完整后端 CI后端 CI 工作流指南中给出的文件名为platform-backend-ci.yml支持workflow_dispatch手动触发可以对任意分支跑完整的 lint / type-check / test coverage 流程gh workflow run platform-backend-ci.yml --ref branch手动运行的价值在于突破路径过滤限制让只改前端/文档的分支也能验证后端套件为一条未触发过后端 CI 的长生命周期分支生成一份新鲜的 coverage 上报上传至该分支 HEAD commit 的 Codecov。刷新已开 PR 的覆盖率状态如果目标分支已有打开的 PR可通过传入pr_number让 coverage 上报挂到该 PR 上触发 Codecov 以 PR base 为参照重新评估codecov/project/platform-backend状态gh workflow run platform-backend-ci.yml --ref pr-head-branch -f pr_numberPR#典型场景某 PR 的codecov/project/platform-backendcheck 为红仅仅是因为该分支从未触发过后端 CICodecov 在拿陈旧的 carried-forward 覆盖率做比较。带pr_number的 dispatch 会为 PR head 生成当前覆盖率并刷新该 check。内部实现上这会把 Codecov action 的override_pr设为该 PR 号对自动事件则保持为空不影响常规的 PR/commit 自动检测。指南还附了一条时序约束workflow_dispatch触发器必须先存在于仓库默认分支master上才能被--ref使用因此pr_number/override_pr的刷新路径要等该变更合入master后才能完整验证——建议用一次真实 dispatch 做验证。故障排查指南的 Troubleshooting 部分给出三类常见问题这里完整继承并结合源码补充快照不匹配Snapshot Mismatches仔细审查 diff若变化是预期的poetry run pytest --snapshot-update若非预期修复产生差异的代码而不是更新快照。异步测试问题Async Test Issues异步函数测试确保使用pytest.mark.asyncioasyncio_mode auto下为双保险Mock 异步函数时用AsyncMockFastAPI TestClient 会自动处理异步路由。另一个从配置中可得的隐性线索若某测试卡住超过 5 分钟faulthandler_timeout 300会自动 dump 所有线程栈此时应从栈中找谁在等一个永不返回的调用而不是盲目怀疑测试本身慢。导入错误Import Errors确认依赖已写入 pyproject.toml注意仓库注释要求新依赖按字母序插入执行poetry install保证依赖已安装核对 import 路径。小结AutoGPT Platform 后端的测试体系可以概括为三层pytest全栈含异步与 mock负责框架pytest-snapshot负责 API 响应的回归基线两层conftest.py根级 backend/conftest.py 管数据库与用户 fixture、backend/api/conftest.py 管鉴权与快照 fixture负责隔离与复用。快照测试为 API 响应提供了一致性护栏与传统精确断言组合后既能快速捕获回归又保持了测试的可维护性。对贡献者而言最实用的三条纪律是新增路由测试时同步--snapshot-update并在提交前git diff审查快照、鉴权相关测试统一 overrideget_jwt_payload、数据库相关操作始终经由poetry run test走独立的agpt_test库。正如指南结尾所言好的测试和好的代码同等重要。【免费下载链接】AutoGPTAutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.项目地址: https://gitcode.com/GitHub_Trending/au/AutoGPT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表