全栈测试专家的分水岭不在会几种框架,而在能否回答三个问题:测什么最划算、失败了能不能定位、质量能不能卡在合并之前。前者是测试策略,中者是可观测性,后者是工程门禁。下面按层给出可落地代码。
单元测试快而多,接口测试稳而准,E2E 慢而少。专家不做“全覆盖”,而是按故障成本 × 发生概率分配预算:核心交易链路重接口测试,UI 只保留主流程冒烟。比例经验值:单元 60%、接口 30%、E2E 10%。
覆盖率 90% 不代表测试有用,可能全是无效断言。变异测试会故意改坏源码,看测试能否捕获——存活变异体才是真正的测试漏洞。
# calc.py
def discount(price: float, vip: bool) -> float:
if price <= 0:
raise ValueError("price must be positive")
rate = 0.9 if vip else 1.0
return round(price * rate, 2)# test_calc.py
import pytest
from calc import discount
@pytest.mark.parametrize("price,vip,expected", [
(100, True, 90.0), (100, False, 100.0), (0.05, True, 0.05),
])
def test_discount(price, vip, expected):
assert discount(price, vip) == expected
def test_discount_invalid():
with pytest.raises(ValueError):
discount(0, True)pip install pytest mutmut
mutmut run --paths-to-mutate calc.py
mutmut results # 看 survived 的变异体,补断言存活率高说明断言太弱,比如只断言“不抛异常”却没校验返回值。
微服务下最常见的故障是“字段悄悄改了”。消费者驱动契约测试(Pact)让提供方在 CI 里验证自己没违约,比等联调发现早得多。
# consumer 侧定义契约
import pact
def test_order_contract():
(pact.Pact(consumer="web", provider="order-service")
.given("order 1 exists")
.upon_receiving("a request for order 1")
.with_request("GET", "/orders/1")
.will_respond_with(200, body={
"id": pact.Like("1"),
"status": pact.Term("PAID", "CREATED|PAID|CLOSED"),
"amount": pact.Like(99.0),
}))接口测试本身用 httpx + 断言状态码/结构/业务规则,务必覆盖幂等、超时、错误码三类负向场景。
E2E 最大的敌人是 flaky(随机失败)。专家做法:自动等待而非 sleep、失败重试而非忽略、失败留存 trace。
# test_checkout.py (Playwright)
from playwright.sync_api import sync_playwright, expect
def test_checkout():
with sync_playwright() as p:
browser = p.chromium.launch()
ctx = browser.new_context(record_video_dir="videos/", trace="on")
page = ctx.new_page()
page.goto("https://shop.local")
page.get_by_role("button", name="加入购物车").click()
page.get_by_role("link", name="去结算").click()
expect(page.get_by_text("订单已创建")).to_be_visible(timeout=5000)
ctx.close(); browser.close()expect(...).to_be_visible() 自带轮询等待,替代 sleep(3) 能消掉大半 flaky;trace="on" 失败后可回放每一步。
压测不是“打高 QPS”,而是验证在 SLA 下系统是否稳定。用 k6 写脚本,阈值即门禁,不达标 CI 直接失败。
// load.js
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '1m', target: 200 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<300'], // P95 < 300ms
http_req_failed: ['rate<0.01'], // 错误率 < 1%
},
};
export default function () {
const res = http.get('https://api.local/orders?page=1');
check(res, { 'status 200': r => r.status === 200 });
}安全不是上线前扫一次,而是每次提交都扫。SAST 查代码、DAST 查运行态、依赖扫描查供应链。
# 依赖漏洞
pip install pip-audit && pip-audit -r requirements.txt
# 运行态扫描
docker run --rm -t owasp/zap2docker-stable zap-baseline.py \
-t https://staging.local -r zap.html
# 容器镜像
trivy image --severity HIGH,CRITICAL registry.local/app:${GIT_SHA}共享测试库是 flaky 之源。用 Testcontainers 每次起独立数据库,测试完销毁,数据干净、可并行。
import pytest
from testcontainers.postgres import PostgresContainer
@pytest.fixture(scope="session")
def db():
with PostgresContainer("postgres:16") as pg:
yield pg.get_connection_url()所有检查汇总成一条流水线,任一不达标即阻断合并。
# .github/workflows/quality.yml
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install -r requirements.txt
- run: pytest --cov=app --cov-fail-under=80
- run: mutmut run && test $(mutmut results | grep -c survived) -lt 10
- run: k6 run load.js
- run: trivy image --exit-code 1 --severity HIGH,CRITICAL app:test不是“会 Playwright 和 JMeter”,而是:能给出一条分层预算、能定位一次 flaky 的根因、能把质量门禁写进 CI 并让团队不敢绕过。测试的终点不是“用例通过”,而是“上线后没人半夜被叫起来”。把单元变异、契约左移、E2E 稳定、压测阈值、安全扫描、数据隔离、门禁七件事做成标准模板,才算真正跨过“全栈”这道门槛。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。