关于 observability-driven-testing
一项利用生产环境可观测性发现测试缺口、并将真实错误转化为测试的测试技能,结合基于追踪的断言与遥测数据驱动的优先级排序。
主要功能
- 01配置 OpenTelemetry 测试运行器
- 02断言追踪结构与延迟
- 03将生产错误转为测试
- 04按错误率为端点测试排序
- 05分析热点路径覆盖率
适用场景
- 01验证仅缓存请求未访问数据库
- 02根据生产错误创建回归测试
- 03结合错误率和流量确定端点测试优先级
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ petrkindlmann/ qa-skills --skill observability-driven-testing安装后在 Claude Code 中输入/observability-driven-testing确认已加载。
附带文件references/log-and-error-pipeline.mdreferences/trace-assertions.md
详细介绍
快速导航
| 情况 | 前往 |
|---|---|
| 让测试执行发出与应用相关联的跟踪 | OTel 测试运行器设置(references/trace-assertions.md) |
| 断言调用了哪些服务/不存在错误 span/延迟 | 将跟踪作为测试证据 |
| 将 Sentry/Datadog 错误转化为测试 | 生产错误到测试的流水线 |
| 决定接下来哪些端点需要测试 | 遥测驱动的测试优先级排序 |
| 跟踪断言不稳定,或 span 始终未到达 | 故障模式 |
探索问题
先检查 .agents/qa-project-context.md。如果该文件存在,请将其作为上下文,并跳过其中已有答案的问题。
可观测性技术栈:
- 使用了什么 APM/跟踪工具?(Datadog、New Relic、Honeycomb、Splunk Observability/SignalFx、ServiceNow Cloud Observability — 原 Lightstep、Dash0、Jaeger、Grafana Tempo、原生支持 OpenTelemetry)——这决定了如何获取跟踪,以及诊断工作流使用哪种查询语法。
- 应用中是否埋入了 OpenTelemetry,哪些服务已埋点?——未埋点的服务不可见,也无法通过跟踪进行测试。
- 使用了什么日志基础设施?(ELK、Loki、CloudWatch、Datadog Logs)——这决定了在哪里通过跟踪 ID 关联日志。
- 使用结构化日志还是自由格式文本?——结构化日志可解析为测试缺口;自由格式文本则需要先进行指纹识别。
跟踪成熟度:
- 是否能获取跨服务边界的分布式跟踪?——若不能,只能进行单服务 span 断言。
- 跟踪采样率是多少?(100%、10%、基于头部、基于尾部)——概率采样会随机丢弃测试所断言的跟踪;必须强制采样测试流量(见“故障模式”)。
- 能否按错误状态、延迟阈值或自定义属性搜索跟踪?
- 跟踪是否与日志和指标相关联?——这可以启用示例(指标 → 代表性跟踪 ID),让优先级排序更具体。
生产错误跟踪:
- 使用什么错误跟踪工具?(Sentry、SmartBear Insight Hub — 原 Bugsnag、Rollbar、Datadog Error Tracking、LaunchDarkly Observability — 包含会话回放,原 Highlight.io)
- 如何对生产错误进行分类处理?(自动、手动、忽略)
- 是否有将生产错误转化为测试用例的流程?
- 最近一次本应由测试捕获的生产错误是什么?
测试基础设施:
- 测试能否发出遥测数据?(跟踪、自定义指标、结构化日志)
- 测试结果是否与应用遥测数据相关联?
- 是否有测试到代码的覆盖率映射?——这是计算下文错误率与覆盖率矩阵的必要条件。
核心原则
1. 生产数据决定测试优先级
最有价值的测试能够防止真实的生产错误,而不是理论上的边缘情况或刻意编造的场景。生产错误日志是一份经过优先级排序的待办清单,列出了本应编写的测试,并按照真实用户实际遇到的问题排序。
2. 跟踪是测试证据
“API 返回了 200”只能证明端点有响应。“请求命中了缓存、跳过了数据库,并在 <50ms 内返回”才能证明系统在每一层都按预期运行。跟踪能让测试更深入,而不会增加测试的不稳定性。
3. 可观测性缺口就是测试缺口
没有跟踪、日志或指标的代码路径是不可见的——在生产环境中无法测试,在事件期间也无法验证。可观测性覆盖率和测试覆盖率是同一个问题的两个视角。
4. 闭合反馈循环
完整周期:发现错误 → 分析 → 编写测试 → 部署 → 防止错误再次发生。如果团队发现了生产环境错误,却没有系统地创建测试,同类错误就会再次发生。
将跟踪数据作为测试证据
将
@opentelemetry/semantic-conventions固定到一个确切版本,并将 sem-conv 升级视为破坏性变更。 跟踪断言通过字符串引用属性名称;这些名称会在不同版本间变化,导致断言在不报错的情况下失效。v1.41.0(2026 年 4 月)引入了 GenAI 破坏性变更和process.executable实体拆分,并将graphql.document从 Recommended 调整为 Opt-In。固定具体版本,并有意进行升级:// package.json — 固定确切版本,不使用插入符 "@opentelemetry/semantic-conventions": "1.41.1"npm install --save-exact @opentelemetry/[email protected]不要引入新的 OpenTracing 兼容垫片。 OTel 规范于 2026 年 3 月弃用了 OpenTracing 兼容层(最早也要到 2027 年 3 月才会移除);新的插桩应使用原生 OTel API 和 OTLP。
以下三种模式均见 references/trace-assertions.md:
- 在测试基础设施中集成 OpenTelemetry — 创建实际的测试 span,并通过
service.name、test.suite和test.run_id将其与应用跟踪关联起来。在同一工作进程中初始化并等待sdk.shutdown()完成。协调器的全局 teardown 无法刷新工作进程中的 SDK。使用工作进程 fixture 或测试进程 teardown 钩子,然后验证最后一个预期 span 已到达收集器;仅执行 shutdown 不能证明数据已送达。 - 基于跟踪数据的断言 — 对跟踪结构、span 属性和时序进行断言(调用了哪些服务、没有 ERROR span、根 span 延迟、DB 操作),而非只检查 HTTP 状态。对于单元级 span 检查,使用进程内的
InMemorySpanExporter+SimpleSpanProcessor并同步读取getFinishedSpans()——无需网络、无需waitForTrace,也不会因超时而偶发失败。跨进程跟踪才使用真实收集器 +waitForTrace流程。 - 跨服务的分布式跟踪验证 — 使用
assertTraceStructure辅助函数,验证请求是否按预期顺序流经各个服务,并检查每个 span 的属性和maxDuration。
对于声明式的基于跟踪数据的断言(通过 YAML/UI 驱动,而非手写 span 查询),开源项目 Tracetest(kubeshop/tracetest)仍可用,但最近一次公开的开源版本是 v1.7.1(2024 年 10 月),近期活动较少——采用前请先评估其维护情况。Tracetest 的商业 Cloud 服务已于 2024 年 10 月结束生命周期;不要设置 Tracetest Cloud,用户会遇到一个已停止的产品。
日志驱动的测试设计
分析生产错误日志,找出测试缺口
生产环境错误是创建测试时优先级最高的输入。每个未处理的错误都意味着缺少一个测试。请参阅 references/log-and-error-pipeline.md,了解 analyze-production-errors.ts 脚本;该脚本会将每个生产错误映射到测试覆盖情况,根据发生频率和最近发生时间分配优先级,并根据错误特征建议测试层级(单元测试/集成测试/e2e 测试)。
对错误分类:已覆盖与未覆盖
1. Export production errors from error tracker (Sentry, Insight Hub, etc.)
- Filter: last 30 days, count > 5 (ignore one-off errors)
- Group by: error message fingerprint
2. For each error group:
a. Does a test exist that would catch this error?
→ Yes: the test is either not running or has a gap (investigate)
→ No: this is a test gap (create a test)
b. What layer should the test live at?
→ TypeError, null reference → unit test
→ Timeout, connection error → integration test with fault injection
→ UI rendering error → E2E test
→ Data inconsistency → contract test or database test
3. Output: prioritized list of tests to create, ordered by:
error frequency × user impact × recency
按错误频率和影响确定测试创建优先级
根据频率(高/低)和影响(高/低)组成的 2×2 矩阵确定优先级:P0 = 高频 + 高影响(立即修复),P1 = 低频 + 高影响(下个 sprint),P2 = 高频 + 低影响(本 sprint),P3 = 两者都低(待办事项)。影响指标:高 = 支付/身份验证失败、数据丢失、崩溃;低 = UI 小问题、响应缓慢但仍可正常使用。
遥测驱动的测试优先级排序
按错误加权差距对端点评分
根据实际使用量和实际故障情况按比例投入测试精力。Gap Score 是本技能中通用的标准公式:
Gap Score = (error_rate × requests_per_day) / max(test_count, 1)
该公式按错误加权:它根据端点产生的失败请求绝对数量进行排名,并除以现有测试覆盖率。(如果你想采用按流量加权的视角,找出流量高但运行健康的端点,则将 error_rate 替换为 (1 + error_rate)——这是在回答不同的问题,并非矩阵标签的含义。)
错误率与端点测试覆盖率映射
Endpoint | Requests/day | Error Rate | Test Count | Gap Score
POST /api/orders | 50,000 | 0.3% | 2 | 75 CRITICAL
PUT /api/profile | 5,000 | 1.2% | 1 | 60 CRITICAL
DELETE /api/items | 2,000 | 0.8% | 0 | 16 HIGH
POST /api/auth | 80,000 | 0.1% | 8 | 10 OK
GET /api/search | 200,000 | 0.05% | 15 | 6.7 OK
Gap Score = (error_rate × requests_per_day) / max(test_count, 1)
Labels: CRITICAL ≥ 50, HIGH 12–49, OK < 12.
Action: create tests for endpoints at HIGH or above, highest score first.
以上每个标签都根据公式和所述阈值推导而来——复制公式即可精确复现该矩阵。你可以选择自己的阈值,但必须说明;切勿手动标注。
示例可补全指标 → 跟踪 → 测试的闭环。 当此矩阵中出现高错误率端点时,OTel 示例可让你从错误率指标直接跳转到具有代表性的失败跟踪 ID,然后沿着该跟踪(见下文)编写测试,而不必手动查找匹配的跟踪。
热路径分析
识别生产环境中遍历次数最多的代码路径,并确保其测试覆盖率与之相称。
1. Extract top 20 endpoints by request volume from APM data
2. For each endpoint, trace the code path through services
3. Map each service-level span to test coverage data
4. Identify hot paths with zero or low test coverage
Output:
/api/checkout → cart-service → pricing-service → payment-service
Coverage: cart-service (82%) → pricing-service (45%) → payment-service (91%)
Gap: pricing-service discount calculation has 45% coverage on a critical path
Action: Add tests for discount edge cases in pricing-service
将端点级流量数据与持续性能分析结合,找出端点内部的 CPU 和内存分配热点,而不只是边界处的热点。OTel 的性能分析信号于 2026-03-26 进入公开 alpha 阶段(OTLP 路径
/v1development/profiles),GA 计划于 2026 年第三季度推出——请勿将其视为已可用于生产环境。如今可用于生产环境的替代方案:Pyroscope、Parca、Polar Signals、Datadog Profiling。无需插桩的 eBPF 性能分析工具(无需更改 SDK):Polar Signals、Parca、Grafana Beyla。
零插桩可观测性——如果无法添加 OTel SDK,eBPF 工具可通过内核系统调用捕获 HTTP/gRPC 跟踪,无需更改代码:Beyla(Grafana)、Cilium Tetragon、Pixie、Coroot。对于 SDK 部署需要数个季度的遗留服务或多语言服务,这类工具很有用。
OTel Weaver 可根据语义约定 YAML 生成类型安全的插桩代码,使跟踪断言与语义约定升级保持同步。如果你要维护自定义约定,或遇到版本间属性漂移,值得采用。
生产错误到测试的流程
本技能中最重要的工作流:将生产环境中的错误转化为可防止问题再次发生的测试。
1. ERROR DETECTED
Source: Sentry, Datadog, CloudWatch, or any error tracker
Capture: error message, stack trace, request context, trace ID, user impact
2. REPRODUCE
- Pull the trace from the observability platform (exemplar → trace ID if available)
- Identify the exact request parameters and state that triggered the error
- Reproduce locally or in staging with equivalent input
- If not reproducible: add targeted logging and wait for recurrence
3. WRITE TEST
- Choose the right layer (unit for logic bugs, integration for service interactions)
- Test must fail before the fix (red-green verification)
- Document the originating production error in the test name or a comment
4. FIX AND DEPLOY
- Fix the bug; verify the test passes with the fix
- Deploy fix + test together
5. VERIFY ELIMINATION
- Monitor the same error in production after deploy
- Confirm error count drops to zero
- If it recurs: the fix was incomplete, repeat from step 2
请参阅 references/log-and-error-pipeline.md,其中展示了一个完整测试,该测试基于 Sentry 问题 PROJ-4521(配送地址为空 → 空引用),在 API 层断言返回 400 或 422(取决于你的契约),并检查端到端结账提示。测试名称和注释会记录错误来源、发生频率及上下文——从生产信号创建测试时应遵循这一惯例。
建立团队反馈闭环
- 每周错误回顾(30 分钟): 从错误跟踪器中按发生频率提取前 10 个新错误。针对每个错误:指定负责人、创建测试,或标记为已知/可接受。错误跟踪器中堆积数千条无人查看的未解决条目,是一种反面模式。
- 事件关闭门槛: 每次复盘都添加“什么测试可以避免这次问题?”,并在事件关闭前创建该测试(或明确记录这一缺口)。将其纳入检查清单,以便审核,而不是仅停留在设想层面。
诊断工作流
端到端跟踪失败的请求
当测试失败或生产环境发生错误时,使用跟踪准确了解发生了什么。
1. Get the trace ID (from test output, error tracker, or user report)
2. Open the trace in your APM tool
- Jaeger: /trace/{traceId}
- Datadog: /apm/traces?traceId={traceId}
- Honeycomb: query by trace.trace_id
3. Walk the span tree
- Root span: what did the user request?
- Child spans: which services were called?
- Error spans: where did it fail? (which span FIRST shows an error)
- Slow spans: where did latency accumulate?
4. Correlate with logs
- Filter logs by trace ID to see every log entry for this request
- Look for warnings or errors that precede the failure
5. Identify the root cause
- Is the error in your code, a dependency, or infrastructure?
- Transient failure or persistent bug?
将测试失败与生产遥测数据相关联
测试失败时,查询可观测性平台:(1)搜索匹配的生产错误消息(最近 7 天);(2)搜索同一 HTTP 路由且状态为 ERROR 的追踪记录。如果有匹配项 → 说明该 Bug 确实存在且正在影响用户,应优先修复。如果没有匹配项 → 可能是仅影响测试的问题,或是尚未出现在生产环境中的新 Bug。这样就能把“可能是偶发问题”转化为“确认影响生产环境”或“仅影响测试的问题”。
反模式
1. 忽视生产信号
错误跟踪器中有 500 个未解决的错误,无人查看;测试套件通过了,团队便认为质量没问题。**修复方法:**每周进行一次上述 30 分钟的错误审查——查看新增错误中排名前 10 的错误,并为每个错误指定负责人、测试,或标记为已知/可接受。
2. 只测试容易观测的内容
团队断言 HTTP 状态和响应时间,却忽略数据一致性、后台任务完成情况和缓存一致性。**修复方法:**为后台任务、缓存操作和异步工作流添加 span,然后对其进行断言。如果某项操作在生产环境中运行,就应该产生遥测数据。
3. 生产环境与测试之间没有反馈循环
SRE 负责处理错误,QA 负责编写测试,双方却没有系统地共享信息,导致同类 Bug 反复出现。**修复方法:**建立从生产错误到测试的流程,以及事件关闭门禁。
4. 添加过多测试监控,却不处理数据
产生了成千上万条指标和日志,却无人分析——徒增成本,没有收益。**修复方法:**先明确你希望测试遥测数据回答的三个具体问题,再构建对应的仪表板;只有提出新问题时才添加监控。
5. 仅将追踪记录用于调试,而不用它们进行断言
追踪记录被当作故障后的调试工具,而非用于防止故障的断言依据。**修复方法:**在集成测试中添加基于追踪记录的断言——检查调用的服务是否正确、查询是否高效,以及是否命中预期的缓存。这些断言可以捕获 HTTP 层断言无法发现的回归问题。
6. 针对概率采样的追踪记录进行断言
头部采样/概率采样会随机丢弃测试正在断言的追踪记录,导致间歇性失败。**修复方法:**对测试流量强制采样(OTEL_TRACES_SAMPLER=always_on 或使用每个请求的覆盖设置),确保记录每一条要断言的追踪记录。
故障模式
| 症状 | 可能原因 | 修复或检查 |
|---|---|---|
waitForTrace 超时,span 始终未到达 | 采样、导出、收集器保留策略,或实际上没有产生 span | 产生该 span;保留测试流量;确认导出器/收集器已收到数据,并检查运行 ID |
| 缺少上一个测试结束时产生的 span | 清理操作未等待完成,或在另一个进程中运行 | 在拥有 SDK 的工作进程中等待 sdk.shutdown() 完成;断言收集器已收到最终数据 |
| 应用的 span 不属于测试的追踪记录 | 应用未传播 traceparent 标头 | 确认应用读取/转发了 W3C traceparent;检查是否已配置 OTel 传播器 |
针对 db.system/graphql.document/GenAI 属性的断言突然失败 | sem-conv 版本升级重命名或移动了该属性 | 将 @opentelemetry/semantic-conventions 固定为精确版本;对比发行说明;有意更新断言 |
| CI 中没有 span 到达收集器 | CI 网络无法访问 OTEL_EXPORTER_ENDPOINT | 指向 CI 内收集器的地址;使用下方的验证步骤执行冒烟测试 |
尽管确实存在错误,status?.code === 'ERROR' 仍无匹配结果 | 收集器将状态序列化为 2/'STATUS_CODE_ERROR',而非 'ERROR' | 匹配收集器实际输出的值(参见 references/trace-assertions.md 中的说明) |
验证
在信任任何跟踪断言之前,先按从小到大的顺序证明遥测链路正常:
# 1. Start a local collector, point the runner at it, run one instrumented test.
OTEL_EXPORTER_ENDPOINT=http://localhost:4318/v1/traces \
OTEL_TRACES_SAMPLER=always_on \
npx playwright test --grep @trace
# 2. Confirm a span with service.name=integration-tests arrived at the collector
# (check the collector's debug/logging exporter output, or query your APM).
然后,在代码中,先断言一个已知跟踪 ID 能够解析,再依赖任何结构性断言:await collector.waitForTrace(traceId, { timeout: 10_000 }) 必须返回跨度 — 如果超时,请先修复采样、刷新或传播问题(参见“故障模式”),再添加更多断言。对于单元级跨度检查,InMemorySpanExporter 路径无需收集器即可同步返回跨度。
完成标准
- 流量排名前 20 的每个端点(来自热门路径矩阵)都能在采样跟踪中解析出至少一个跨度 — 没有任何高流量端点不可见。
- 至少一个关键用户旅程有基于跟踪的断言,验证服务调用和跨度属性(而不仅仅是 HTTP 状态),并且在
OTEL_TRACES_SAMPLER=always_on下通过。 - 针对生产错误分析中发现的已知故障模式,已编写由日志分析启发的测试用例。
- 已计算错误率/Gap-Score 矩阵,并据此得出至少一组按优先级排序的未测试代码路径。
@opentelemetry/semantic-conventions在package.json中固定为确切版本(不带插入符)。- 已记录部署后审查(检查清单项目或事后分析条目),确认在将版本标记为稳定之前检查了可观测性信号。
参考文件(位于 references/ 中)
- trace-assertions.md — OTel 测试运行器设置(包含正确的全局 teardown 刷新和内存导出器替代方案)、强制采样说明、基于跟踪的断言,以及分布式
assertTraceStructure助手。 - log-and-error-pipeline.md —
analyze-production-errors.ts测试缺口脚本,以及一个完整的生产错误转化为测试的示例(400 或 422 断言)。
相关技能
- testing-in-production — 发布期间的安全渐进式发布技术(标志、金丝雀、护栏指标);而本技能使用这些发布产生的遥测数据作为输入,在发布之后设计测试。
- synthetic-monitoring — 发布之后运行的定时探测,它们本身也会生成遥测数据;这些遥测数据会用于此处的分析。
- qa-metrics — 将遥测数据推导出的数值(错误率、延迟、Gap Score)转化为质量仪表板和 KPI;本技能生成原始信号,qa-metrics 对其进行汇总。
- ai-bug-triage — 当输入是一批需要分类和分流的 CI/生产故障时使用;先用它为此处的错误分类步骤提供输入,然后再回来编写测试。