AI Test Strategy Architect
AI-powered test strategy and automation assistant — design comprehensive testing frameworks, generate unit/integration/e2e test cases, implement test automation for web/mobile/API,…
lingfeng-19
@gechengling
Install
$ openclaw skills install @gechengling/ai-test-strategy-architectAI Test Strategy Architect
Overview
Build testing that catches bugs before users do. This AI-powered testing assistant designs robust test strategies, generates comprehensive test cases, and implements automation frameworks—turning quality assurance from a bottleneck into a competitive advantage.
Language Policy(语言策略 / SQP-3)
本技能同时提供中英双语触发词与内容,语言使用规则如下,避免输出语言飘移:
| 项目 | 规则 |
|---|---|
| 默认语言 | 跟随提问语言:中文提问用中文回答,英文提问用英文回答 |
| 术语 | 保留英文原词(property-based testing、page object、flaky),首次出现时括注中文 |
| 代码与配置 | 一律原样保留,不翻译标识符、字符串与注释语言之外的部分 |
| 混合提问 | 以正文主体语言为准;若仍不明确,直接询问一次,不要自行猜测 |
| 表格与模板 | 同一份产物内保持单一语言,不要中英混排 |
| 切换 | 用户任何时候说“用英文/用中文回答”,立即切换,不影响技术内容 |
Triggers
- 中文触发词:
测试策略、单元测试、集成测试、E2E测试、自动化测试、测试用例、TDD、BDD、Playwright测试、Selenium、测试覆盖率 - English triggers:
test strategy,unit testing,integration testing,e2e testing,automated testing,test cases,TDD,BDD,Playwright,Selenium,test coverage,CI/CD testing
Required context(至少满足一条才激活)
- 需要设计或改进一套测试策略、分层比例或门禁;或
- 需要为具体代码/接口/流程设计用例;或
- 需要把测试接入 CI/CD 并设定质量门禁;或
- 需要诊断测试体系本身的健康问题(覆盖率虚高、flaky、断言强度不足)。
Non-triggers(不激活)
| 请求 | 为什么不归本技能 | 应转向 |
|---|---|---|
| “帮我修这个 bug” | 缺陷修复,不是测试设计 | 通用调试 |
| “这个接口返回 500,帮我看看” | 生产问题排查 | 运维/日志排查 |
| “压测一下能扛多少 QPS” | 性能工程,目标与方法都不同 | 性能测试工具(k6/Locust) |
| “做一次安全渗透测试” | 安全测试需专门授权与方法 | 安全测试流程 |
| “把这份需求文档总结一下” | 文档处理,无测试对象 | 文档摘要 |
| “安装 Playwright 环境” | 环境搭建操作 | 官方安装文档 |
执行边界声明:本技能输出测试代码、配置形态与策略文档,供你在自己的环境中 审阅后运行。它不执行测试、不写入你的仓库、不访问你的 CI。文中代码仅作参考材料。
Features
1. Test Strategy Design
- Assess project requirements and risk profiles
- Design tailored testing pyramids (unit/integration/e2e ratios)
- Select appropriate testing frameworks per use case
- Define test data management strategies
- Create test environment specifications
Worked example — risk-based scope for a payments-style service
| Module | 失效影响 | 发生概率 | 风险分 | 测试投入 | 具体手段 | 门禁归属 |
|---|---|---|---|---|---|---|
| 保费计算 | 资金错误,监管风险 | 中 | 9 | 最高 | 属性测试 + 黄金样本回归 + 变更检测 | PR 阻断 + nightly 变异测试 |
| 投保校验 | 单用户受阻 | 高 | 6 | 高 | 边界表 + 参数化 | PR 阻断 |
| 支付回调 | 重复扣款 | 低 | 6 | 高 | 幂等性专项 + 故障注入 | PR 阻断 + nightly 故障注入 |
| 报表导出 | 内部使用 | 中 | 3 | 中 | 快照测试 | nightly 快照比对 |
| 邮件通知 | 体验问题 | 中 | 2 | 低 | 冒烟 + 手动抽查 | 不设门禁,仅冒烟 |
| 管理后台筛选 | 内部低效 | 低 | 1 | 最低 | 冒烟即可 | 不设门禁 |
评分方式:影响(1–5)× 概率(1–2),≥8 必须有属性测试与变更检测, ≤3 只做冒烟。这样把预算集中在真正会出钱的地方,而不是平均撒。
测试环境规格清单(常被忽略的 5 项)
- 数据种子能否一键重建(不能就要先解决这个,否则一切自动化都是脆弱的)
- 第三方依赖是否有稳定的测试替身(沙箱/契约/Mock 三选一)
- 环境之间配置差异是否有单一来源(配置漂移是"本地能跑线上挂"的主因)
- 测试数据是否含脱敏后的真实分布(用全是合法值的假数据测不出边界)
- 每个环境的重置成本与时限(重置超过 10 分钟的流水线会被绕过)
2. Test Case Generation
- Generate unit tests from code functions/methods
- Create integration test scenarios from API specs
- Design end-to-end user journey tests
- Build property-based tests for edge cases
- Generate negative test cases (error handling)
Worked example — 一个参数的完整用例矩阵(参数化而非堆砌)
以 calculate_discount(price, discount_percent, is_loyal) 为例,不要写 12 个
独立函数,用一张表驱动:
| price | discount_percent | is_loyal | 期望 | 覆盖的意图 |
|---|---|---|---|---|
| 100.00 | 0 | False | 100.00 | 恒等 |
| 100.00 | 20 | False | 80.00 | 基本路径 |
| 100.00 | 20 | True | 76.00 | 忠诚用户叠加 |
| 100.00 | 100 | False | 0.00 | 上边界 |
| 0.00 | 50 | False | 0.00 | 零值 |
| 99.99 | 33 | False | 66.99 | 舍入 |
| -10.00 | 10 | False | ValueError | 负价 |
| 100.00 | -5 | False | ValueError | 下越界 |
| 100.00 | 150 | False | ValueError | 上越界 |
对应的参数化写法要点:把"期望异常"也放进同一张表,用哨兵值标记, 不要在参数化之外再补一批独立的异常测试——那会让两套用例逐渐分叉。
属性测试该测什么(property-based,不是随机测试)
对同一个函数,属性测试关心的是不变量,例如:
- 对任意合法输入,
0 <= result <= price - 对任意合法输入,
discount_percent单调不减时,result单调不增 - 对任意合法输入,
result == round(result, 2) - 对任意合法输入,同样参数调用两次结果相同(纯函数性)
最小可运行的 Hypothesis 形态:
from hypothesis import given, strategies as st
@given(
price=st.floats(min_value=0, max_value=1e6, allow_nan=False),
pct=st.floats(min_value=0, max_value=100, allow_nan=False),
loyal=st.booleans(),
)
def test_result_within_bounds(price, pct, loyal):
r = calculate_discount(price, pct, loyal)
assert 0 <= r <= price
assert r == round(r, 2)
注意:这段代码是给你在自己环境运行的参考材料,本技能不代为执行。
3. Test Automation Implementation
- Scaffold test projects with proper structure
- Implement page object models for UI tests
- Set up API test frameworks with data-driven approaches
- Configure test parallelization and distribution
- Implement visual regression testing
Worked example — page object 的正确粒度
反例(把业务流程塞进 page object,导致一改就全崩):
class CheckoutPage:
def buy_whole_flow(self, card): # 不要这样
...
正例(page object 只暴露页面能力,流程留在测试里):
class ProductPage:
def __init__(self, page): self.page = page
def add_to_cart(self):
self.page.get_by_test_id("add-to-cart").click()
def cart_count(self) -> int:
return self.page.get_by_test_id("cart-item").count()
class CheckoutPage:
def __init__(self, page): self.page = page
def pay_with(self, card: str, expiry: str, cvv: str):
self.page.get_by_label("card number").fill(card)
self.page.get_by_label("expiry").fill(expiry)
self.page.get_by_label("cvv").fill(cvv)
self.page.get_by_test_id("place-order").click()
def order_number(self) -> str:
return self.page.get_by_test_id("order-number").inner_text()
判据:page object 里不应出现断言(assert 属于测试),也不应出现跨页面跳转。
跳转留在测试用例里,页面对象只负责"这一页能做什么"。
并行化的三条硬约束
- 数据隔离:并行 worker 不能共用同一条测试数据,用 worker id 生成唯一前缀。
- 端口与临时目录:并行时端口冲突是最常见的"随机失败",必须动态分配。
- 共享服务:若必须共用一个外部服务(如支付沙箱),要么串行,要么限流—— 否则你会得到一批看起来像 flaky 的资源竞争失败。
4. CI/CD Pipeline Integration
- Design testing stages in CI/CD pipelines
- Configure test reporting and dashboards
- Set up automated quality gates
- Implement canary/feature flag testing strategies
- Create performance test thresholds in pipelines
Worked example — 四阶段流水线与门禁
| 阶段 | 运行内容 | 时限 | 失败处理 | 门禁指标 |
|---|---|---|---|---|
| commit (pre-push) | lint + 单元测试(受影响模块) | ≤ 60 s | 阻断提交 | 0 失败 |
| PR | 全量单元 + 集成 + 契约 | ≤ 8 min | 阻断合并 | 覆盖率不低于基线 |
| merge (main) | 全量 + E2E 冒烟 | ≤ 20 min | 阻断发布流水线 | 冒烟 100% 通过 |
| nightly | 全量 E2E + 性能 + 变异测试 | ≤ 2 h | 次日处理,不阻断 | 变异分趋势不下降 |
关键设计:把慢的、不稳定的放 nightly,把快的、确定的放 PR。 如果 PR 阶段超过 8 分钟,工程师会开始绕过它——这是流水线失效的真实起点。
质量门禁配置示例(概念形态,按你的 CI 语法调整)
quality_gates:
coverage:
metric: line
floor: 72 # 当前实际值 + 2,不要一上来写 90
ratchet: true # 只允许上升,不允许回落
exclude: ["generated/*", "migrations/*"]
flakiness:
quarantine_threshold: 0.01 # 100 次里失败 1 次即隔离
quarantine_expiry_days: 14 # 隔离不是删除,到期必须处理
performance:
p95_latency_ms: 800
regression_tolerance: 0.10 # 允许 10% 波动,超过即失败
blocking:
- name: critical_path_e2e
on_failure: stop_pipeline
- name: visual_diff
on_failure: require_human_approval # 视觉差异必须人看
注意:以上为配置形态示例,需在用户自己的 CI 环境中运行与调整。
Workflow
Comprehensive Test Strategy Workflow
Phase 1: Assessment
├── Analyze project architecture
├── Identify critical user flows
├── Assess technical risks
├── Define quality metrics
└── Select testing tools
Phase 2: Design
├── Design test pyramid
├── Define test scope per layer
├── Create test data strategy
├── Document test environment needs
└── Plan test automation approach
Phase 3: Implementation
├── Set up test project structure
├── Implement unit tests
├── Build integration test suite
├── Create e2e test scenarios
└── Configure test runners
Phase 4: Automation
├── Integrate with CI/CD
├── Set up test reporting
├── Configure parallel execution
├── Implement test monitoring
└── Create quality dashboards
Phase 5: Maintenance
├── Review test effectiveness
├── Optimize slow tests
├── Update for new features
└── Archive obsolete tests
Quick Test Generation Workflow
1. INPUT: Source code or feature description
↓
2. ANALYZE: Identify testable units
- Functions/methods
- User interactions
- API endpoints
↓
3. GENERATE: Create test cases
- Happy path scenarios
- Edge cases
- Error scenarios
- Boundary conditions
↓
4. VALIDATE: Run tests, fix failures
↓
5. OPTIMIZE: Improve coverage and speed
Pyramid Ratios by Project Type
The classic 60/30/10 split is a starting point, not a law. Use this instead:
| 项目类型 | Unit | Integration | E2E | 额外层 | 理由 | 常见误判 |
|---|---|---|---|---|---|---|
| 纯前端 Web 应用 | 50% | 20%(组件/契约) | 30% | 视觉回归 | UI 是主要风险面,E2E 占比自然更高 | 把视觉回归当成 E2E 的替代品 |
| 后端 API 服务 | 60% | 35% | 5% | 契约测试 | 逻辑在服务端,E2E 只需关键链路 | 用 E2E 去覆盖所有业务分支 |
| 数据/ETL 管道 | 45% | 25% | 5% | 25% 数据质量测试 | 行数、空值率、分布漂移比功能更重要 | 只测转换函数,忽略数据质量层 |
| 移动端 | 55% | 20% | 25% | 设备矩阵冒烟 | 设备与系统版本碎片化推高 UI 层 | 只在一台真机上跑冒烟就算设备覆盖 |
| 金融/保险核心 | 65% | 25% | 10% | 属性测试 + 审计测试 | 计算正确性优先,且需可追溯 | 只靠单元测试,缺审计留痕测试 |
| 内部工具 | 40% | 30% | 30% | 探索式测试 | 用户少、变更快,重自动化反而拖慢 | 照搬 60/30/10 做全套自动化 |
如何判断比例是否合适
- E2E 占比高且经常红 → 逻辑下沉不足,把验证前移到集成层
- 单元覆盖高但线上仍有事故 → 断言太弱,引入变异测试检验"断言强度"
- 集成层几乎为零 → 多半是"只能整体起来才能测",这是架构问题不是测试问题
Input Examples
Example 1: Function to Unit Test
Input Code:
def calculate_discount(price: float, discount_percent: float, is_loyal: bool) -> float:
"""
Calculate final price after discount.
Args:
price: Original price
discount_percent: Discount percentage (0-100)
is_loyal: Whether customer is a loyalty member
Returns:
Final price after discount
"""
if price < 0:
raise ValueError("Price cannot be negative")
if discount_percent < 0 or discount_percent > 100:
raise ValueError("Discount must be between 0 and 100")
discount_multiplier = 1 - (discount_percent / 100)
if is_loyal:
# Loyalty members get extra 5% off
final_multiplier = discount_multiplier * 0.95
else:
final_multiplier = discount_multiplier
return round(price * final_multiplier, 2)
Generated Test Cases:
import pytest
from your_module import calculate_discount
class TestCalculateDiscount:
"""Unit tests for calculate_discount function"""
# Happy path tests
def test_no_discount_returns_original_price(self):
"""No discount should return the original price"""
assert calculate_discount(100.00, 0, False) == 100.00
def test_standard_discount(self):
"""Basic discount calculation"""
assert calculate_discount(100.00, 20, False) == 80.00
def test_loyal_customer_extra_discount(self):
"""Loyal customers get additional 5% off"""
result = calculate_discount(100.00, 20, True)
expected = round(100 * 0.80 * 0.95, 2) # 76.00
assert result == expected
def test_full_discount(self):
"""100% discount should result in 0"""
assert calculate_discount(100.00, 100, False) == 0.00
# Edge cases
def test_zero_price(self):
"""Zero price should return zero"""
assert calculate_discount(0.00, 50, False) == 0.00
def test_rounding_to_cents(self):
"""Prices should round to 2 decimal places"""
result = calculate_discount(99.99, 33, False)
assert result == round(99.99 * 0.67, 2)
# Error cases
def test_negative_price_raises_error(self):
"""Negative price should raise ValueError"""
with pytest.raises(ValueError, match="Price cannot be negative"):
calculate_discount(-10.00, 10, False)
def test_negative_discount_raises_error(self):
"""Negative discount should raise ValueError"""
with pytest.raises(ValueError, match="Discount must be between"):
calculate_discount(100.00, -5, False)
def test_over_100_discount_raises_error(self):
"""Discount over 100% should raise ValueError"""
with pytest.raises(ValueError, match="Discount must be between"):
calculate_discount(100.00, 150, False)
# Boundary tests
def test_boundary_zero_percent_discount(self):
"""0% is valid discount"""
assert calculate_discount(50.00, 0, False) == 50.00
def test_boundary_hundred_percent_discount(self):
"""100% is valid discount"""
assert calculate_discount(50.00, 100, False) == 0.00
Example 2: API Integration Test
Input: OpenAPI endpoint definition
paths:
/api/v1/users/{userId}/orders:
get:
summary: Get user orders
parameters:
- name: userId
in: path
required: true
schema:
type: string
- name: status
in: query
schema:
type: string
enum: [pending, shipped, delivered]
responses:
'200':
description: List of orders
'401':
description: Unauthorized
'404':
description: User not found
Generated Integration Test:
import pytest
import requests
from typing import Dict
BASE_URL = "https://api.example.com"
HEADERS = {"Authorization": "Bearer <test_token>"}
class TestUserOrdersAPI:
"""Integration tests for User Orders endpoint"""
@pytest.fixture
def test_user_id(self):
"""Fixture providing a test user with known orders"""
return "user_123"
def test_get_orders_success(self, test_user_id: str):
"""Should return 200 with list of orders"""
response = requests.get(
f"{BASE_URL}/api/v1/users/{test_user_id}/orders",
headers=HEADERS
)
assert response.status_code == 200
data = response.json()
assert "orders" in data
assert isinstance(data["orders"], list)
def test_get_orders_with_status_filter(self, test_user_id: str):
"""Should filter orders by status"""
response = requests.get(
f"{BASE_URL}/api/v1/users/{test_user_id}/orders",
params={"status": "pending"},
headers=HEADERS
)
assert response.status_code == 200
orders = response.json()["orders"]
assert all(order["status"] == "pending" for order in orders)
def test_get_orders_unauthorized(self, test_user_id: str):
"""Should return 401 without valid token"""
response = requests.get(
f"{BASE_URL}/api/v1/users/{test_user_id}/orders"
)
assert response.status_code == 401
def test_get_orders_user_not_found(self):
"""Should return 404 for non-existent user"""
response = requests.get(
f"{BASE_URL}/api/v1/users/nonexistent_user/orders",
headers=HEADERS
)
assert response.status_code == 404
assert "error" in response.json()
Example 3: E2E Test with Playwright
API 风格提醒(Python 与 JS 不同名,混用会静默失效)
Playwright 的 Python 绑定用 snake_case,JavaScript/TypeScript 用 camelCase。 同一个断言在两种语言里名字不同:
| 意图 | Python | JS / TS |
|---|---|---|
| 可见 | expect(loc).to_be_visible() | expect(loc).toBeVisible() |
| 数量 | expect(loc).to_have_count(1) | expect(loc).toHaveCount(1) |
| 文本 | expect(loc).to_have_text("ok") | expect(loc).toHaveText("ok") |
| 非空 | expect(loc).not_to_be_empty() | expect(loc).not.toBeEmpty() |
Python 里写 toBeVisible() 不会报语法错误,但会抛 AttributeError;更糟的情况是
被 IDE 自动补全"修正"成一个存在但语义不同的方法。复制示例时先确认语言绑定。
下方示例统一使用 Python(snake_case)风格。
Input: User journey description
User flow: Login -> Add item to cart -> Checkout -> Verify order confirmation
Generated E2E Test:
import pytest
from playwright.sync_api import Page, expect
@pytest.fixture
def logged_in_page(page: Page):
"""Fixture that logs in user before each test"""
page.goto("https://shop.example.com/login")
page.fill('[name="email"]', "test@example.com")
page.fill('[name="password"]', "testpassword123")
page.click('[type="submit"]')
page.wait_for_url("**/dashboard")
return page
def test_complete_checkout_flow(logged_in_page: Page):
"""End-to-end test: Login -> Add to cart -> Checkout -> Confirmation"""
page = logged_in_page
# Step 1: Browse to product
page.goto("https://shop.example.com/products/widget-pro")
page.click('[data-testid="add-to-cart"]')
# Step 2: Verify cart
page.click('[data-testid="cart-icon"]')
expect(page.locator('[data-testid="cart-item"]')).to_have_count(1)
# Step 3: Proceed to checkout
page.click('[data-testid="checkout-button"]')
page.fill('[name="shipping_address"]', "123 Test Street")
page.fill('[name="zip_code"]', "12345")
page.click('[data-testid="continue-payment"]')
# Step 4: Complete payment
page.fill('[name="card_number"]', "4242424242424242")
page.fill('[name="expiry"]', "12/28")
page.fill('[name="cvv"]', "123")
page.click('[data-testid="place-order"]')
# Step 5: Verify confirmation
expect(page.locator('[data-testid="order-confirmation"]')).to_be_visible()
expect(page.locator('[data-testid="order-number"]')).not_to_be_empty()
Output Templates
Template: Test Strategy Document
# Test Strategy Document
## Project Overview
- Project Name: [Name]
- Version: [Version]
- Test Scope: [What's in/out]
## Quality Objectives
| Metric | Target | Measurement |
|--------|--------|-------------|
| Code Coverage | >80% | Codecov |
| Bug Escape Rate | <5% | Bug Tracker |
| Test Execution Time | <10 min | CI Pipeline |
## Test Pyramid
╱╲
╱ ╲
╱ E2E╲ [Few - 10%]
╱──────╲
╱Integration╲ [Some - 30%]
╱────────────╲
╱ Unit Tests ╲ [Many - 60%]
╱────────────────╲
## Testing Tools
| Layer | Tool | Language |
|-------|------|----------|
| Unit | Pytest | Python |
| Integration | pytest | Python |
| E2E | Playwright | TypeScript |
| API | REST Assured | Java |
## Test Environments
- Dev: https://dev.example.com
- Staging: https://staging.example.com
- Production: https://example.com
## Test Data Strategy
- [Strategy details]
## Release Criteria
- [ ] All critical tests pass
- [ ] Coverage meets target
- [ ] No P0 bugs open
Example 4: 数据管道(ETL)的测试策略
Input:
我们有一个每日跑批:从业务库抽数 → 清洗 → 计算保费指标 → 写入报表库。
目前没有测试,每次出问题都要人工对数。怎么设计测试?
Output:
ETL 的测试重点不是"函数对不对",而是"数据对不对"。分四层:
第 1 层:转换逻辑单元测试(占比 40%)
把清洗与计算函数抽成纯函数(输入 DataFrame → 输出 DataFrame),
用小型固定样本断言。关键是让转换逻辑不依赖 IO。
第 2 层:数据质量测试(占比 30%)—— 大多数团队缺的就是这层
| 检查 | 断言 | 失败含义 |
|---|---|---|
| 行数波动 | 今日行数在近 30 日 P10–P90 区间内 | 上游抽数缺失 |
| 主键唯一 | 无重复保单号 | 关联放大(fan-out) |
| 空值率 | 关键字段空值率 < 阈值 | 上游字段变更 |
| 分布漂移 | 金额分布 PSI < 0.2 | 口径或业务突变 |
| 汇总对账 | 明细合计 == 汇总表 | 口径不一致 |
| 时间完整性 | 每个分区都有数据 | 跑批漏跑 |
第 3 层:端到端小样本回归(占比 20%)
用 100 条脱敏真实数据跑完整管道,与上一次的黄金结果文件比对。
黄金文件需要有明确的更新流程,否则会变成"永远批准"。
第 4 层:生产监控(占比 10%)
把第 2 层的检查搬到生产,作为数据契约告警。
三个最常见的坑:
1. 用"全是合法值"的假数据 —— 测不出脏数据路径,务必混入脏样本
2. 没有幂等性测试 —— 同一天重跑两次结果必须一致
3. 把测试写在对生产库的直连上 —— 必须有独立的可重置环境
Example 5: 从"覆盖率 85% 但仍在出事故"说起
Input:
我们覆盖率 85%,质量门禁也过了,但线上还是出低级事故。测试策略哪里有问题?
Output:
覆盖率衡量的是"代码被执行过",不是"结论被验证过"。85% 且仍出事故,
通常是以下四种情况之一:
1. 断言太弱(最常见)
症状:测试只写 assert result is not None
检验:跑一次变异测试(mutmut / Stryker / PIT),变异分低于 40% 即确认
修复:对每个分支断言具体值或具体不变量
2. 测试写成了实现镜像
症状:改实现时测试必须同步改,且改完仍然绿
检验:故意引入一个业务错误,看测试是否变红
修复:从需求/规格推导期望值,不从当前实现推导
3. 关键路径没有端到端覆盖
症状:单元全绿,但串起来就错
检验:列出"出钱/出合规风险"的 3 条链路,看是否有跨层测试
修复:这 3 条链路各补一个端到端用例,其余不必追求
4. 覆盖率数字被稀释
症状:DTO、generated、migrations 计入分母
修复:排除这些目录后重算,通常真实覆盖率会显著下降
两周可落地的改进顺序:
- 第 1 天:排除 generated/migrations,重算真实基线
- 第 2–3 天:对核心模块跑一次变异测试,拿到变异分
- 第 4–8 天:给 3 条关键链路各补 1 个端到端用例
- 第 9–10 天:把覆盖率门禁从"绝对值"改为"棘轮"(只升不降)
判断改进是否有效的唯一标准:变异分上升,而不是覆盖率上升。
Example 6: 门禁设在哪里才不会被绕过
Input:
我们把覆盖率门禁设成 85%,结果工程师开始写"只为覆盖率"的测试。
门禁是不是设错了?
Output:
问题不在阈值,在门禁挂错了指标,也挂错了阶段。
三个常见错误与修正:
1. 门禁挂在"绝对值"上 → 改为棘轮(ratchet)
绝对值会让团队围绕数字优化。棘轮只要求"不比上一次低",
把激励从"刷数字"改成"别变差"。
2. 门禁挂在"全量覆盖率"上 → 改为关键路径覆盖率
全量分母里混着 DTO、generated、migrations,谁都能靠补这些拉高数字。
只统计"出钱/出合规风险"的模块,数字会下降但可信度上升。
3. 门禁挂在"唯一阶段"上 → 分阶段分层挂
PR 阶段只挂快且确定的(单元 + 集成 + 契约,≤ 8 分钟);
慢的挂 nightly 且不阻断;视觉差异挂人工审批。
PR 阶段超过 8 分钟,工程师就会开始绕过它。
再补一条反刷分规则:新增代码的行覆盖率单独统计,
不允许用存量代码的高覆盖率去掩盖新增代码的低覆盖。
验证门禁是否健康的可观测信号:
- 门禁失败后,有多少次是"改了测试"而不是"改了代码"?前者占比过高即为刷分
- 变异分是否随覆盖率一起上升?不上升说明新增测试没有断言强度
Best Practices
For Test Design
- Follow FIRST principles: Fast, Independent, Repeatable, Self-validating, Timely
- Name tests descriptively:
test_user_cannot_login_with_invalid_password - Test one thing per test: Easier debugging and maintenance
- Use data-driven tests: Reduce duplication with parameterized tests
- Test edge cases: Empty inputs, null values, maximum limits
For Test Automation
- Prioritize stability: Flaky tests are worse than no tests
- Keep tests fast: Slow tests don't run often
- Use page objects: Encapsulate UI structure changes
- Isolate tests: No shared state between tests
- Clean up after yourself: Reset what you change
For CI/CD Integration
- Fail fast: Run fastest tests first
- Parallelize: Split tests across workers
- Report properly: Generate actionable reports
- Set quality gates: Block releases below thresholds
- Monitor trends: Track flakiness over time
Ecosystem Status (as of 2026-09-28 / 截至 2026-09-28)
Tool versions and best practice shift quickly. Verify against official release notes before committing to a stack.
| 关注点 | 需要确认什么 | 影响 |
|---|---|---|
| 浏览器自动化版本 | Playwright / Cypress / Selenium 当前主版本 | 等待机制与选择器 API 差异 |
| 变异测试工具链 | 与你的语言和测试框架是否兼容 | 决定能否把变异分纳入门禁 |
| CI 运行成本 | 并行 worker 的计费与上限 | 决定金字塔各层能跑多频繁 |
| 视觉回归方案 | SaaS 还是自托管 | 涉及截图是否出网(受监管行业敏感) |
| AI 生成测试的合入政策 | 本单位对生成代码的评审要求 | 决定人工检查点设在哪里 |
最近动态(截至 2026-09-28,以官方发布为准)
- 变异测试与"断言强度"指标正从学术研究走向工程实践,部分团队已用它替代 单纯的覆盖率门禁。
- 视觉回归的自托管方案受关注度上升,主要驱动是数据驻留与合规要求。
- AI 生成测试用例已成常见能力,行业关注点转向"生成用例的评审与可追溯", 而非生成数量。
- 质量门禁的口径正在从"覆盖率绝对值"转向"棘轮 + 关键路径覆盖率 + 变异分" 的组合,单纯提高覆盖率阈值被普遍认为会产生反向激励。
- AI 生成测试用例的评审成为落地瓶颈:多数团队反馈瓶颈不在生成速度,而在 "生成用例是否与需求一致、是否可追溯",评审环节的人工成本被低估。
- 以上为趋势描述,具体工具版本与能力请以官方最新发布为准。
Testing Framework Comparison
| Framework | Best For | Languages | 学习曲线 | 并行能力 | 调试体验 | 适合团队规模 | 主要坑 | 何时不要用 | 多语言 API 差异风险 |
|---|---|---|---|---|---|---|---|---|---|
| Pytest | Python APIs, unit tests | Python | 低 | 中(pytest-xdist) | 好(--pdb、丰富报错) | 1–20 | fixture 作用域误用导致状态泄漏 | 需要浏览器级交互时 | 无(纯 Python) |
| Jest | JS/TS 单元与组件测试 | JS/TS | 低 | 好(内置) | 好 | 1–20 | mock 过深,测到 mock 而非实现 | 需要真实浏览器行为时 | 无(纯 JS/TS) |
| Vitest | Vite 生态单元测试 | JS/TS | 低 | 好 | 好 | 1–15 | 与 Jest 的 mock API 差异 | 非 ESM/老构建体系 | 无(纯 JS/TS) |
| JUnit 5 | Java 应用 | Java | 中 | 中 | 中 | 5–50 | 扩展模型复杂,配置易失控 | 快速原型阶段 | 无(纯 Java) |
| Playwright | Web E2E | TS, Python, Java | 中 | 好(内置分片) | 很好(trace viewer) | 2–30 | 自动等待让人误解为"不需要断言状态" | 需测极老浏览器 | 高:Python snake_case 与 JS camelCase 断言不同名 |
| Cypress | Web E2E(开发体验优先) | JS/TS | 低 | 需付费/自建 | 很好(时间旅行) | 1–15 | 多标签、多域限制 | 跨域/多窗口场景 | 无(仅 JS/TS) |
| Selenium | 遗留浏览器与网格 | 多语言 | 高 | 好(Grid) | 差(报错不直观) | 10+ | 等待与驱动版本地狱 | 新项目(有更好选择) | 中:多语言绑定的命名并不总是一致 |
| REST Assured | API 测试 | Java, Groovy | 中 | 好 | 中 | 5–30 | DSL 冗长,维护成本高 | 团队不以 Java 为主 | 无(Java/Groovy) |
| SuperTest | Node API 测试 | JavaScript | 低 | 好 | 好 | 1–10 | 只覆盖 HTTP 层,无契约能力 | 需要跨语言契约时 | 无(JavaScript) |
| Pact | 消费者驱动契约测试 | 多语言 | 中高 | 好 | 中 | 5–30 | Broker 运维被低估 | 单体应用、调用方单一 | 中:各语言 DSL 差异较大 |
| Hypothesis | 属性/不变量测试 | Python | 中 | 好 | 中(反例收缩优秀) | 2–20 | 策略写不好等于随机测试 | 纯 CRUD、无计算逻辑 | 无(仅 Python) |
| k6 / Locust | 性能与负载 | JS / Python | 中 | 好 | 中 | 2–20 | 把负载测试当功能测试跑 | 只需要单次基准时 | 中:k6 用 JS,Locust 用 Python,脚本不可互换 |
选型判据(比"哪个更流行"更可靠):团队已有语言 > 调试体验 > 并行能力 > 生态插件。调试体验决定日常成本,流行度不决定。
Version History
- 1.0.1 (2026-09-10)
- Added worked examples to all four feature groups (risk-based scope table and environment checklist; parameterised case matrix and property-based invariants; page-object granularity and parallelism constraints; four-stage pipeline with quality-gate config)
- Added "Pyramid Ratios by Project Type" (6 project types, 6 columns) and a self-check for whether your ratio is right
- Expanded framework comparison from 3 to 10 columns and 8 to 12 rows (learning curve, parallelism, debugging, team size, main pitfall, when not to use)
- Added Example 4 (ETL/data-pipeline strategy incl. data-quality checks) and Example 5 (high coverage but still shipping incidents)
- Added "Ecosystem Status (as of 2026-09-10)"
- Corrected Python Playwright assertions (
to_have_count,not_to_be_empty)
- 1.0.2 (2026-09-28)
- Fixed a real code defect in the Playwright example:
toBeVisible()(JS/TS camelCase) inside a Python snippet corrected toto_be_visible(), and added a Python vs JS/TS assertion-name table explaining why the mix-up fails silently (SDI-4) - Added a Language Policy section covering default-language, terminology, code, mixed-language and switching rules (SQP-3)
- Narrowed triggers with a required-context rule, a six-row non-trigger table and an explicit execution-boundary statement (SQP-1)
- Added a 门禁归属 column to the risk-based scope table, a 常见误判 column to the pyramid-ratio table and a 多语言 API 差异风险 column to the framework comparison
- Added Example 6 on where to place quality gates so they are not bypassed
- Ecosystem status updated to 2026-09-28 with two added dynamics
- Fixed a real code defect in the Playwright example:
- 1.0.0 (2026-05-15): Initial release
- Test strategy design framework
- Unit test generation
- Integration test scaffolding
- E2E test patterns (Playwright)
- CI/CD integration guidance
Last Updated: 2026-09-28
Top skills in this category
API Gateway
@byungkyuCall third-party APIs through the Maton gateway, which injects the credential for an app the user has already connected. Use this skill when the user names a connected app and a concrete action in it - read a mailbox, query a CRM, file an issue, update a spreadsheet, etc.
OpenClaw Command Center
@jontsaiMission control dashboard for OpenClaw - real-time session monitoring, LLM usage tracking, cost intelligence, and system vitals. View all your AI agents in one place.
GitHub
@byungkyuGitHub API integration with managed OAuth. Access repositories, issues, pull requests, commits, branches, and users. Use this skill when users want to interact with GitHub repositories, manage issues and PRs, search code, or automate workflows. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Calls run through the `maton` CLI with OAuth login, or over raw HTTP with a Maton API key where the CLI cannot be installed. Every call is authenticated as the user's connection and reaches only what that connection's authorization allows, which the provider enforces on every request; the endpoints documented here are the ones this skill uses, and any other endpoint of this app needs the user to ask for it by name. Default to read and list calls, and confirm every write or new connection with the user. This file also documents the three constructs that turn a GitHub connection into automation, in the order they are used: the connection (the first step), a hosted function that runs a GitHub action through the Maton SDK, and a trigger that calls that function on a schedule or on an event. Those sections are the platform's own reference text, shared with the api-gateway skill, with GitHub examples; they add no GitHub capability - GitHub is itself an event source, so its own events are in `references/github/triggers.md`, and the files under `references/<source>/triggers.md` are the platform's event catalogues for the sources Maton offers (time, Calendly, GitHub, Gmail, HubSpot, Linear, Notion, Slack, Stripe).
google-slides
@byungkyuGoogle Slides API integration with managed OAuth. Create presentations, add slides, insert content, and manage slide formatting. Use this skill when users want to interact with Google Slides. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Calls run through the `maton` CLI with OAuth login, or over raw HTTP with a Maton API key where the CLI cannot be installed. Every call is authenticated as the user's connection and reaches only what that connection's authorization allows, which the provider enforces on every request; the endpoints documented here are the ones this skill uses, and any other endpoint of this app needs the user to ask for it by name. Default to read and list calls, and confirm every write or new connection with the user. This file also documents the three constructs that turn a Google Slides connection into automation, in the order they are used: the connection (the first step), a hosted function that runs a Google Slides action through the Maton SDK, and a trigger that calls that function on a schedule or on an event. Those sections are the platform's own reference text, shared with the api-gateway skill, with Google Slides examples; they add no Google Slides capability - Google Slides is not an event source, a trigger cannot read Google Slides data, and the files under `references/<source>/triggers.md` are the platform's event catalogues for the sources Maton offers (time, Calendly, GitHub, Gmail, HubSpot, Linear, Notion, Slack, Stripe).
Interview Simulator
@wscatsSimulates mock interviews for any role and experience level with tailored technical, behavioral, and case questions plus detailed feedback and scoring.