ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Sentry 仓库 Python 测试指南:测试目录组织、工厂方法、EAP 时间窗口与备份覆盖规范

Sentry 仓库 Python 测试指南:测试目录组织、工厂方法、EAP 时间窗口与备份覆盖规范 Sentry 仓库 Python 测试指南测试目录组织、工厂方法、EAP 时间窗口与备份覆盖规范【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry本文以 tests/AGENTS.md由 tests/CLAUDE.md 通过AGENTS.md引用的 Python Testing Guide为主体结合 Sentry 仓库中的testutils、flake8 插件与tests/snuba等源码实现系统讲解 Sentry 后端 Python 测试的编写规范。读者将掌握新测试用例应放在哪里、如何用工厂方法替代直接建表、如何规避日期漂移与 EAP 降采样陷阱、以及备份/迁移模型测试的强制覆盖要求。一、引言Sentry 测试规范的核心脉络Sentry 是一个 developer-first 的错误跟踪与性能监控平台其后端测试体量庞大tests/目录下有 2800 个 Python 测试文件与 1482 个 pysnap 快照。为了保证测试在本地、Snuba CI 与 Sentry CI 中行为一致仓库以AGENTS.md体系沉淀了一套强制的 Python 测试规范。本文面向两种读者一是给 Sentry 提交代码的开发者需要知道测试写在哪、怎么写、会被什么 lint 规则拦住二是想借鉴大型 Python 项目测试工程实践的工程师Sentry 在测试文件定位、工厂方法、时间稳定性date-stable tests、EAP 数据集窗口、备份/迁移模型覆盖等方面给出了可复制的方案。规范的总入口是 tests/CLAUDE.md它只有一行AGENTS.md指向 tests/AGENTS.md——这才是完整的 Python Testing Guide。仓库根目录的 AGENTS.md 同时给出了命令执行指引所有 Python 命令必须在 virtualenv 中运行如.venv/bin/pytest ...测试命令参见其 Command Execution Guide 一节而前端 React/TypeScript 测试*.spec.tsx、RTL、MockApiClient则走独立的react-testingskill不在本文范围。二、新测试用例放哪里tests/ 镜像 src/ 的定位规则规范首先解决测试文件该放哪的问题规则非常机械且严格代码位置src/sentry/foo/bar.py→ 测试位置tests/sentry/foo/test_bar.py做法是把tests/前缀拼到源码路径前再把模块名加上test_前缀即给路径加tests/、给模块名加test_。这一镜像结构在 tests/AGENTS.md 的 File Location Map 一节被再次强调Python 测试的tests/目录镜像src/目录结构fixtures 放在fixtures/{type}/工厂类集中在tests/sentry/testutils/factories.py。特例强约束保证 Snuba 兼容性的测试必须放在tests/snuba/因为该目录下的测试还会在 Snuba 的 CI 中运行。这保证了 Sentry 前端调用 Snuba 的契约在两边都被验证。关于必须加到已有测试文件而非新建规范要求修复 bug 或新增功能时把测试用例加到已有的测试文件中而不是新建文件。这一做法保持了测试文件的收敛性也让tests/与src/的镜像结构始终可预测。三、测试基类与标准模式APITestCase 示例规范给出一个标准测试模式示例位于tests/sentry/core/endpoints/test_organization_details.pyfrom sentry.testutils.cases import APITestCase class OrganizationDetailsTest(APITestCase): endpoint sentry-api-0-organization-details def test_get_organization(self): org self.create_organization(ownerself.user) self.login_as(self.user) response self.get_success_response(org.slug) assert response.data[id] str(org.id)从源码看cases.py 聚合了 Sentry 测试所需的一切APITestCase继承自rest_framework.test.APITestCase、before_now时间助手、EAPClient、load_data从sentry.utils.samples加载事件样本等self.create_organization实际由 factories.py 中的Factories.create_organization提供。两条硬性注解测试必须是**纯过程式procedural**的禁止分支逻辑——后端测试中几乎永远不需要if语句。如果发现测试里需要条件分支通常是测试设计有问题的信号。self.get_success_response(org.slug)这类断言型请求助手会在响应非 2xx 时直接失败天然保证了测试即断言。四、时间稳定性禁止把当前/未来年份硬编码进测试现在S015 规则是 Sentry 测试工程里最容易踩的坑之一其动机与 Snuba 的保留期retention机制强相关不要在模块级或类级作用域或freeze_time(datetime(...))中把当前或未来 UTC 日历年份硬编码为测试的现在。那会随时间漂移进 Snuba 的保留期之外。正确做法用before_now(...)或now - timedelta构造相对时间刻意构造历史 fixture 时使用较早的固定年份函数体内的固定时间戳fixtures、断言是允许的。before_now定义在src/sentry/testutils/helpers/datetime.py并被 cases.py 大量导入使用例如测试中用before_now(minutes9)造数据。lint 侧的实现这条规则由 tools/flake8_plugin.py 以S015编码实现源码第 110 行起的注释与_s015_msg表明S015 会标记在模块/类作用域或freeze_time(...)中使用大于等于当前 UTC 年份的字面量。该插件还实现了 S001~S024 一系列 Sentry 专属 lint 规则如 S004 禁止assertRaises、S014 禁止直接用unittest.mock等通过prek.venv/bin/prek run -q统一执行。这也是时间稳定测试能够被机器强制而非仅靠代码评审的原因。五、EAP / Snuba 端点测试30 天保留期与降采样陷阱这是本指南技术含量最高的一节直接关系到测试能否稳定通过。5.1 问题背景两套默认窗口的冲突Snuba 的 EAPEvents/Attributes/Profilingoutcomes 路由把标准保留期默认设为 30 天当查询起点早于该窗口时强制走 tier 8降采样/下采样存储。而 Sentry API 对未显式指定的窗口默认是90 天。于是Snuba 集成测试若省略显式窗口就会少算近期数据且epm()/eps()/tpm()这类速率函数会除以错误的窗口导致断言全错。5.2 共享默认值EAPClient 与 30d 注入EAPClient/EAP_DEFAULT_STATS_PERIODeap.py会给 EAP 数据集和已知 EAP 路径注入statsPeriod EAP_FULL_FIDELITY_QUERY_DAYS即30d。继承OrganizationEventsEndpointTestBase的测试套件还会通过client_get()/do_request()默认落到同一个30d窗口——这覆盖了 EAPClient 启发式规则漏掉的路径例如某些 trace-meta 路由。从源码看常量定义在 constants.pyEAP_FULL_FIDELITY_RETENTION_DAYS 30EAP_FULL_FIDELITY_QUERY_DAYS与之相等注释明确写着这是仍能命中 tier 1 的最宽窗口Snuba 会对起点早于 31 天的查询做降采样而FULL_RETENTION_ITEM_TYPESuptime results 与 preprod是从不降采样的类型。EAPClient的实现eap.py展示了注入逻辑的细节只有查询中没有任何窗口键statsPeriod、statsPeriodStart/End、start、end、range、timestamp时才注入通过dataset/itemType/data_source判断是否 EAP 数据集对完整保留数据集如preprodSize不注入对路径片段/trace-items/、/ai-conversations/、/spans/fields/、/traces/做路径级兜底GET 请求会原样保留 query string避免把#重编码成%23。5.3 对这些套件的硬性要求优先使用client_get()/do_request()或调用它们的本地助手不要裸调self.client.get(...)——flake8S020会直接标记。S020 的实现同样在 tools/flake8_plugin.py它针对继承OrganizationEventsEndpointTestBase或OrganizationEventsTraceEndpointBase的类且路径命中tests/snuba/api/endpoints/test_organization_*强制走会注入默认statsPeriod的助手。真实用例见 test_organization_events.pyclient_get通过with_default_stats_period在未显式传窗口时补上statsPeriod EAP_DEFAULT_STATS_PERIODdo_request则负责登录 feature 开关 请求。查询窗口保持 ≤30d除非测试有意覆盖长期保留/降采样行为。速率断言epm/eps/tpm/…必须使用真实请求窗口通常是EAP_FULL_FIDELITY_QUERY_DAYS不能拿更短的硬编码周期。若必须查询 30d要同时设置窗口并且要么传standard_retention_days最多 90要么显式断言 tier-8 / 降采样行为。不得为了 CI 变绿而削弱断言。六、用工厂方法替代直接Model.objects.createSentry 测试的另一个硬约束是禁止直接调用Model.objects.create必须按优先级使用工厂Fixture 方法如self.create_model来自sentry.testutils.fixtures.Fixtures等基类工厂方法sentry.testutils.factories.Factories当 fixture 不可用时使用。规范给出的 diff 示例展示了正确做法- direct_project Project.objects.create( - organizationself.organization, - nameDirectly Created, - slugdirectly-created - ) direct_project self.create_project( organizationself.organization, nameDirectly Created, slugdirectly-created # Note: Ensure factory args match )理由直接建表绕过了共享的测试设置逻辑。从源码看factories.py 提供了一整套create_organization第 512 行起、create_project第 694 行起等方法内部会补全创建组织/项目所需的一连串附属记录API key、规则、书签等fixtures 层Fixtures再把这些工厂方法包装成self.create_*测试助手。这也与用pytest而不是unittest的规范相互呼应pytest.raises取代assertRaises减少样板代码并复用工厂中定义的共享设置逻辑- self.assertRaises(ValueError, EffectiveGrantStatus.from_cache, None) with pytest.raises(ValueError): EffectiveGrantStatus.from_cache(None)顺带一提assertRaises本身也会被 flake8 插件以S004规则拦截。七、备份/迁移Backup/Relocation测试的模型覆盖凡是__relocation_scope__不等于RelocationScope.Excluded的模型tests/sentry/backup/下的备份测试套件都会自动检查新增这类模型或给已有模型加字段而不更新以下内容CI 就会失败穷举 fixturesbackups.py在对应的create_exhaustive_*方法中至少创建一个该模型的实例——org/project 作用域的模型放create_exhaustive_organization源码第 440 行起user 作用域的放create_exhaustive_user第 379 行起。否则tests/sentry/backup/test_exhaustive.py以及test_exports.py/test_imports.py中的ScopingTests会报 Someexpected_modelsentries were not found 或 models were not included in the export。比较器Comparators如果模型有导入时会变化的字段如DefaultFieldsModel的date_added/date_updated要在get_default_comparators()comparators.py中注册DateUpdatedComparator(date_updated, date_added)该类定义于同文件第 188 行。否则test_exhaustive_dirty_pks会因这些字段的UnequalJSON差异而失败。覆盖检查tests/sentry/backup/test_coverage.py可能还会要求若模型有不基于 Organization/Global 作用域外键的唯一约束需在test_imports.py中加碰撞测试COLLISION_TESTED若__relocation_scope__是一组作用域set需在test_models.py中加动态迁移作用域测试DYNAMIC_RELOCATION_SCOPE_TESTED。导入/导出/diff 循环与比较器如何运作详见 tests/sentry/backup/README.md。这套机制的本质是任何可被备份导出的模型都必须证明自己能被完整导出、导入且 diff 干净从而保证组织级数据迁移relocation的可靠性。八、测试生态补充Kafka/Arroyo 与 File Location Map8.1 Kafka/Arroyo 组件的测试方式规范在 Testing Best Practices 中给出对 Kafka/Arroyo 组件使用LocalProducerMemoryMessageStorage而非 mock。这与 Sentry 大量依赖消息队列事件流、任务 broker 等的架构一致——用真实的内存存储验证消息的生产/消费语义比 mock 更能捕获序列化与契约问题。8.2 文件位置速查File Location Map类型位置Python 测试tests/镜像src/结构Fixturesfixtures/{type}/工厂类tests/sentry/testutils/factories.py结合仓库根目录 AGENTS.md 的 Context-Aware Loading 一节测试相关代码tests/**/*.py、src/**/tests/**/*.py应遵循本文档tests/AGENTS.md后端src/**/*.py遵循 src/AGENTS.md前端遵循 static/AGENTS.md。AGENTS.md是 AI Agent 指令的权威来源——新增或修改 agent 指引时应更新对应 AGENTS.md而不是写进编辑器规则文件。九、本地执行与检查清单根目录 AGENTS.md 给出了配套的执行方式必须在 virtualenv 中运行# 环境准备SENTRY_DEVENV_FRONTEND_ONLY1 跳过迁移仅测试足够 SENTRY_DEVENV_FRONTEND_ONLY1 devenv sync direnv allow devservices up # 运行单个测试文件不要裸跑 pytest会非常慢 .venv/bin/pytest -n3 -svv --reuse-db tests/sentry/api/test_base.py # 提交前 lint自动检测改动文件 .venv/bin/prek run -q写测试时对照以下清单自查测试文件是否放在tests/sentry/module/test_name.pySnuba 兼容性测试放tests/snuba/是否添加到已有测试文件而非新建是否纯过程式、无分支逻辑是否用before_now等相对时间避免 S015 拦截当前/未来年份EAP 套件是否用client_get()/do_request()避免 S020窗口是否 ≤30d速率断言是否用真实窗口是否用 fixture/工厂方法而非Model.objects.create是否用pytest而非unittest新增 relocation-scope 模型时是否同步更新穷举 fixtures、比较器及碰撞/动态作用域测试十、总结Sentry 的 Python 测试规范可以用四句话概括位置镜像tests/对src/Snuba 特例另置、时间稳定相对时间优先S015 机器强制、窗口对齐EAP 查询锁定 30d tier-1S020 强制走注入助手、覆盖闭环模型备份测试由穷举 fixtures comparators 碰撞测试自动校验。这套规范不是孤立的约定而是与 tools/flake8_plugin.py 的 S 系列 lint、testutils 的共享基建以及 Snuba 的保留期机制深度咬合——理解底层动机才能写出既通过 CI、又真实反映生产行为的测试。【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进