unit-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<objective> Write unit tests that fail when the code is wrong and pass when it is right — nothing weaker. A test that mocks every collaborator stays green while the integration is broken; the doubles taxonomy below stops that. A `coverageThreshold` typo (or the plural `coverageThresholds`, which Jest silently ignores) lets 40%-covered code ship on a green pipeline; the config and Verification sections below make the gate actually fire. This skill covers Jest, Vitest, and pytest: doubles, coverage gating, snapshots, fake timers, and mutation testing as a behavior check on top of coverage. </objective>
<objective> 编写的单元测试需满足:代码错误时测试失败,代码正确时测试通过——标准不能降低。如果一个测试将所有协作对象都mock掉,那么即使集成逻辑已损坏,测试仍会显示通过;下文的测试替身分类可避免这种情况。`coverageThreshold`的拼写错误(或复数形式`coverageThresholds`,Jest会静默忽略)会让覆盖率仅40%的代码通过绿色流水线发布;下文的配置与验证部分可确保门禁真正生效。本技能涵盖 Jest、Vitest 和 pytest:测试替身、覆盖率门禁、快照测试、假计时器,以及作为覆盖率之上行为校验的突变测试。 </objective>

Discovery Questions

探索问题

Check
.agents/qa-project-context.md
first — if it exists, use it and skip anything answered there.
  1. Framework: Jest, Vitest, or pytest? Check
    package.json
    or
    pyproject.toml
    . The runner decides config keys and mock APIs.
  2. Coverage tooling: Already configured? Look for
    jest.config.*
    ,
    vitest.config.*
    ,
    .nycrc
    ,
    [tool.coverage]
    . Determines whether you add the gate or just tune it.
  3. Mocking strategy: Manual mocks, auto-mocking, or dependency injection? Check for
    __mocks__/
    dirs or DI containers — this sets which doubles you reach for.
  4. Conventions: Co-location (
    *.test.ts
    next to source) or a
    __tests__
    /
    tests/
    tree? Match what exists; don't introduce a third location.

首先查看
.agents/qa-project-context.md
文件——如果存在,使用其中的信息并跳过已解答的问题。
  1. 测试框架: 使用 Jest、Vitest 还是 pytest?查看
    package.json
    pyproject.toml
    文件。测试运行器决定了配置项和mock API的使用方式。
  2. 覆盖率工具: 是否已配置?查找
    jest.config.*
    vitest.config.*
    .nycrc
    [tool.coverage]
    配置。这将决定你是需要添加门禁还是仅调整现有配置。
  3. Mock策略: 手动mock、自动mock还是依赖注入?查找
    __mocks__/
    目录或DI容器——这将决定你选择哪种测试替身。
  4. 代码约定: 测试文件与源代码同目录(
    *.test.ts
    放在源码旁)还是单独的
    __tests__
    /
    tests/
    目录?遵循项目现有约定,不要引入第三种存放位置。

Core Principles

核心原则

1. Test behavior, not implementation. Verify what code does, not how. Refactoring internals should not break tests.
typescript
// Bad — implementation detail        // Good — observable behavior
expect(svc._cache.size).toBe(3);      expect(svc.getUser("abc")).toEqual({ id: "abc", name: "Alice" });
2. Fast, isolated, deterministic. No network/disk/DB. No shared mutable state. No uncontrolled
Date.now()
or
Math.random()
— freeze them with fake timers and seeded values.
3. Arrange-Act-Assert. One clear shape per test.
typescript
it("should apply discount for orders over $100", () => {
  // Arrange
  const order = createOrder({ subtotal: 150 });
  const svc = new DiscountService(0.1);
  // Act
  const result = svc.apply(order);
  // Assert
  expect(result.total).toBe(135);
});
4. One assertion concept per test. Multiple
expect
calls are fine when they verify the same concept.
5. Descriptive names.
"should [behavior] when [condition]"
, not
"test calculateTotal"
.

1. 测试行为,而非实现细节。 验证代码的功能,而非实现方式。重构内部逻辑不应导致测试失败。
typescript
// 不良示例——依赖实现细节        // 良好示例——验证可观测行为
expect(svc._cache.size).toBe(3);      expect(svc.getUser("abc")).toEqual({ id: "abc", name: "Alice" });
2. 快速、独立、可预测。 不依赖网络/磁盘/数据库。无共享可变状态。避免不受控的
Date.now()
Math.random()
——使用假计时器和种子值固定这些变量。
3. Arrange-Act-Assert 模式。 每个测试遵循清晰的三段式结构。
typescript
it("should apply discount for orders over $100", () => {
  // Arrange(准备)
  const order = createOrder({ subtotal: 150 });
  const svc = new DiscountService(0.1);
  // Act(执行)
  const result = svc.apply(order);
  // Assert(断言)
  expect(result.total).toBe(135);
});
4. 每个测试验证一个核心逻辑点。 当多个
expect
调用验证同一逻辑时,是允许的。
5. 测试名称具备描述性。 使用「should [行为] when [条件]」格式,而非「test calculateTotal」这类模糊名称。

Framework-Specific Patterns

框架专属模式

The full setup/teardown, mocking, spying, timer, in-source, and monorepo examples for each runner live in
references/patterns.md
. Below is what is current and what to reach for; copy the code from the reference.
各运行器的完整初始化/清理、mock、spy、计时器、源码内测试、单仓库示例均存于
references/patterns.md
。以下是当前主流版本及推荐用法;代码示例请参考该文件。

Jest

Jest

Current is Jest 30.x (30.4.2, May 2026). Jest 30 added
--collect-tests
,
jest.config.mts
support, Temporal-aware fake timers, and
clearMocksOnScope
. If the code under test uses the Temporal API or time-zone logic, Jest 30's Temporal-aware fake timers remove a class of brittle setup.
Reach for:
jest.mock()
for module boundaries (
jest.requireActual
for partial mocks),
jest.spyOn()
to wrap a real method,
jest.Mocked<T>
for typed mocks, and
jest.useFakeTimers()
for time. See
references/patterns.md
§ Jest.
当前主流版本为 Jest 30.x(30.4.2,2026年5月)。Jest 30新增了
--collect-tests
jest.config.mts
支持、兼容Temporal API的假计时器,以及
clearMocksOnScope
功能。如果被测代码使用Temporal API或时区逻辑,Jest 30的Temporal感知假计时器可避免一类脆弱的初始化问题。
推荐用法:使用
jest.mock()
处理模块边界(使用
jest.requireActual
实现部分mock)、
jest.spyOn()
包装真实方法、
jest.Mocked<T>
实现类型化mock、
jest.useFakeTimers()
处理时间相关逻辑。详见
references/patterns.md
的Jest章节。

Vitest

Vitest

Same API as Jest, Vite-native. Stable: Vitest 4.1.x (June 2026); 5.0.0-beta is out (beta.3, May 2026). Vitest 4 added
coverage.changed
(changed-files-only coverage),
mockThrow
/
mockThrowOnce
, and a stable browser mode. Vitest 5 beta removes the
sequential
option
and requires Node 22 / Vite 6.4 — wait for stable before adopting. Mock with
vi.mock
/
vi.spyOn
; the standout features are in-source testing (
import.meta.vitest
) and browser mode for component rendering. See
references/patterns.md
§ Vitest.
API与Jest兼容,原生支持Vite。稳定版本为 Vitest 4.1.x(2026年6月);5.0.0-beta版本已发布(beta.3,2026年5月)。Vitest 4新增了
coverage.changed
(仅变更文件覆盖率)、
mockThrow
/
mockThrowOnce
,以及稳定的浏览器模式。Vitest 5 beta 移除了
sequential
选项
,并要求 Node 22 / Vite 6.4——请等待稳定版本后再升级。使用
vi.mock
/
vi.spyOn
进行mock;其突出特性为源码内测试
import.meta.vitest
)和浏览器模式用于组件渲染。详见
references/patterns.md
的Vitest章节。

pytest

pytest

Use fixtures +
conftest.py
(with
yield
for teardown),
@pytest.mark.parametrize
for data-driven cases, and
monkeypatch
for env/attr substitution. Prefer fixtures over
setUp
/
tearDown
methods — fixtures compose and isolate per test. See
references/patterns.md
§ pytest.
使用fixtures +
conftest.py
(用
yield
实现清理)、
@pytest.mark.parametrize
实现数据驱动测试、
monkeypatch
替换环境变量/属性。优先使用fixtures而非
setUp
/
tearDown
方法——fixtures具备组合性,且每个测试独立隔离。详见
references/patterns.md
的pytest章节。

Bun / Deno

Bun / Deno

bun test
(Jest-compatible, no extra config) and
deno test
(native TS, permission flags) are reasonable defaults when your runtime is already Bun or Deno. Prefer Vitest/Jest for Node projects with deeper plugin ecosystems.

当运行环境为Bun或Deno时,
bun test
(兼容Jest,无需额外配置)和
deno test
(原生支持TS,带权限控制)是合理的默认选择。对于Node项目,优先使用Vitest/Jest以获得更丰富的插件生态。

Mocking Taxonomy

Mock分类

Pick the simplest double that does the job. Most of the time that is a stub.
DoubleWhat it doesWhen to use
StubReturns canned data, no verificationControl a dependency's return value
SpyWraps real impl, records callsVerify calls without changing behavior
MockReplaces impl + records callsControl return AND verify interaction
FakeSimplified working impl (in-memory DB)Complex stateful dependencies
Rule of thumb: prefer stubs over mocks; reserve fakes for stateful dependencies; never call a real external API in a unit test. Only mock the external boundary (network, filesystem, DB, time) — let fast, deterministic internal collaborators run for real, or you get a suite that is green while the integration is broken. The four doubles in code:
references/patterns.md
§ Test doubles.

选择满足需求的最简测试替身。大多数情况下,stub即可胜任。
测试替身功能适用场景
Stub返回预设数据,不做调用验证控制依赖的返回值
Spy包装真实实现,记录调用情况验证调用行为但不改变原有逻辑
Mock替换原有实现并记录调用情况既控制返回值又验证交互行为
Fake简化的可用实现(如内存数据库)复杂的有状态依赖
经验法则: 优先使用stub而非mock;仅对有状态依赖使用fake;单元测试中绝不调用真实的外部API。仅mock外部边界(网络、文件系统、数据库、时间)——让快速、可预测的内部协作对象真实运行,否则测试套件会在集成逻辑损坏时仍显示通过。四种测试替身的代码示例:
references/patterns.md
的测试替身章节。

Coverage

覆盖率配置

Configuration

配置方法

Jest — the threshold key is
coverageThreshold
(singular). The plural
coverageThresholds
is not a Jest key: Jest ignores it silently, the gate never enforces, and CI stays green at 30% coverage. This is the single most common config bug.
javascript
// jest.config.js
module.exports = {
  coverageProvider: "v8",
  collectCoverageFrom: ["src/**/*.ts", "!src/**/*.{d,test,stories}.ts", "!src/**/index.ts"],
  coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } },
};
Vitest — set
test.coverage.thresholds
in
vitest.config.ts
with
provider: "v8"
(see
references/patterns.md
§ Vitest for the full block).
pytest:
toml
undefined
Jest——阈值配置项为**
coverageThreshold
**(单数形式)。复数形式
coverageThresholds
并非Jest的有效配置项:Jest会静默忽略它,门禁永远不会生效,CI会在覆盖率仅30%时仍显示绿色。这是最常见的配置错误。
javascript
// jest.config.js
module.exports = {
  coverageProvider: "v8",
  collectCoverageFrom: ["src/**/*.ts", "!src/**/*.{d,test,stories}.ts", "!src/**/index.ts"],
  coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } },
};
Vitest——在
vitest.config.ts
中设置
test.coverage.thresholds
,并指定
provider: "v8"
(完整配置块请见
references/patterns.md
的Vitest章节)。
pytest:
toml
undefined

pyproject.toml

pyproject.toml

[tool.coverage.run] source = ["src"] omit = ["src//test_*.py", "src//conftest.py"] [tool.coverage.report] fail_under = 80 show_missing = true exclude_lines = ["pragma: no cover", "if TYPE_CHECKING:"]
undefined
[tool.coverage.run] source = ["src"] omit = ["src//test_*.py", "src//conftest.py"] [tool.coverage.report] fail_under = 80 show_missing = true exclude_lines = ["pragma: no cover", "if TYPE_CHECKING:"]
undefined

Coverage types and what to gate on

覆盖率类型与门禁优先级

TypeMeasuresBlind spots
BranchEvery if/else path taken?Misses value combinations
LineEach line executed?Misses untested branches in one line
StatementEach statement executed?Similar to line
FunctionEach function called?Nothing about correctness
Priority: Branch > Line > Statement > Function. Use 80% line as the baseline gate, not a vanity target, and weight branch coverage higher. Focus coverage on business logic, transformations, error paths, and edge cases; skip generated code, type definitions, barrel exports, trivial getters, and framework boilerplate.
For interpreting which uncovered lines matter and doing gap analysis, that's
coverage-analysis
, not this skill.
类型衡量指标盲区
分支覆盖率是否覆盖所有if/else路径?遗漏值组合场景
行覆盖率是否执行每一行代码?遗漏单行内未测试的分支
语句覆盖率是否执行每个语句?与行覆盖率类似
函数覆盖率是否调用每个函数?无法验证逻辑正确性
优先级: 分支覆盖率 > 行覆盖率 > 语句覆盖率 > 函数覆盖率。使用80%行覆盖率作为基准门禁,而非虚荣指标,并优先关注分支覆盖率。重点覆盖业务逻辑、数据转换、错误路径和边界场景;跳过生成代码、类型定义、桶导出、简单getter和框架模板代码。
如需解读哪些未覆盖行有意义并进行缺口分析,请使用
coverage-analysis
技能,而非本技能。

CI gate

CI门禁

Jest and Vitest exit non-zero when thresholds fail — that exit code IS the gate. pytest needs the flag explicitly:
yaml
- run: pytest --cov=src --cov-fail-under=80

当未达到阈值时,Jest和Vitest会返回非零退出码——该退出码就是门禁。pytest需要显式添加参数:
yaml
- run: pytest --cov=src --cov-fail-under=80

Mutation Testing

突变测试

Coverage tells you what code ran. Mutation testing tells you whether the tests would catch a bug. It makes small source changes (
>
>=
,
true
false
) and reruns the suite against each mutant. If the suite still passes, the mutant survived — your tests executed that logic but did not assert on it.
覆盖率告诉你哪些代码被执行了。突变测试告诉你测试是否能发现bug。它会对源代码进行微小修改(如
>
改为
>=
true
改为
false
),并针对每个变异体重新运行测试套件。如果测试套件仍通过,说明该变异体存活——你的测试执行了这段逻辑,但未对其进行断言验证。

Stryker (JS/TS)

Stryker(JS/TS)

bash
npm i -D @stryker-mutator/core @stryker-mutator/jest-runner  # or vitest-runner
javascript
// stryker.config.json  (Stryker's documented default; .mjs/.mts also load)
{
  "testRunner": "jest",
  "coverageAnalysis": "perTest",
  "mutate": ["src/**/*.ts", "!src/**/*.test.ts"],
  "thresholds": { "high": 80, "low": 60, "break": 50 },
  "reporters": ["html", "clear-text", "progress"]
}
Stryker's own defaults are
{ high: 80, low: 60, break: null }
break: null
means no failing exit. Set
break
(e.g. 50) to make a low score fail CI. Run:
npx stryker run
.
bash
npm i -D @stryker-mutator/core @stryker-mutator/jest-runner  # 或 vitest-runner
javascript
// stryker.config.json (Stryker官方默认配置;也支持.mjs/.mts格式)
{
  "testRunner": "jest",
  "coverageAnalysis": "perTest",
  "mutate": ["src/**/*.ts", "!src/**/*.test.ts"],
  "thresholds": { "high": 80, "low": 60, "break": 50 },
  "reporters": ["html", "clear-text", "progress"]
}
Stryker的默认阈值为
{ high: 80, low: 60, break: null }
——
break: null
意味着低分不会导致CI失败。设置
break
(如50)可让低分触发CI失败。运行命令:
npx stryker run

mutmut (Python) — mutmut 3.x

mutmut(Python)——mutmut 3.x

mutmut 3 dropped the old CLI surface. Configure paths in a
[mutmut]
block, run, then review survivors in the TUI:
ini
undefined
mutmut 3重构了旧版CLI界面。在
[mutmut]
块中配置路径,运行后在TUI中查看存活变异体:
ini
undefined

setup.cfg (or a [tool.mutmut] table in pyproject.toml)

setup.cfg (或在pyproject.toml中添加[tool.mutmut]配置块)

[mutmut] paths_to_mutate=src/

```bash
pip install mutmut          # 3.5.x
mutmut run                  # paths come from config, not a flag
mutmut browse               # interactive TUI: inspect and retest survivors
mutmut apply <mutant_id>    # write a survivor to disk to see what it changed
Avoid:
mutmut run --paths-to-mutate=src/
,
mutmut results
, and
mutmut show 42
— that was the mutmut <3 surface. The
--paths-to-mutate
flag is gone (paths move to the
[mutmut]
config block) and
results
/
show
are replaced by
browse
/
apply
(mutmut 3.5.x, verified June 2026). Following the old commands errors out on a current install.
[mutmut] paths_to_mutate=src/

```bash
pip install mutmut          # 3.5.x版本
mutmut run                  # 路径来自配置,无需命令行参数
mutmut browse               # 交互式TUI:查看并重新测试存活变异体
mutmut apply <mutant_id>    # 将存活变异体写入磁盘,查看具体修改内容
注意: 避免使用
mutmut run --paths-to-mutate=src/
mutmut results
mutmut show 42
——这些是mutmut 3.x之前的用法。
--paths-to-mutate
参数已移除(路径需移至
[mutmut]
配置块),
results
/
show
browse
/
apply
替代(mutmut 3.5.x,2026年6月验证)。在当前版本中使用旧命令会报错。

Interpreting scores

分数解读

ScoreMeaning
90%+Strong — catching most logic changes
70–89%Decent — review survivors in critical paths
<70%Tests execute code but do not verify behavior
Run mutation testing on critical business logic, not the whole codebase (it is slow). Ignore equivalent mutants — logically identical code where no test could ever tell the difference.

分数含义
90%+优秀——能捕获大多数逻辑变更
70–89%良好——需重点检查核心路径的存活变异体
<70%测试仅执行代码,但未验证逻辑正确性
仅对核心业务逻辑运行突变测试,而非整个代码库(速度较慢)。忽略等效变异体——即逻辑上完全相同、测试永远无法区分的代码修改。

Snapshot Testing

快照测试

Use for: UI component render output, serialized data structures, CLI formatting — output where exact structure matters and is tedious to assert field-by-field.
Do not use for: frequently changing output (snapshot fatigue → rubber-stamp reviews), large snapshots (unreviewable), implementation details (CSS classes, internal IDs), or as a substitute for a targeted assertion when one specific value is what matters.
Prefer inline snapshots for small output (<20 lines) and property matchers (
expect.any(String)
) for dynamic fields like ids and timestamps. Always run CI with
--ci
so an unknown snapshot fails instead of being silently written and committed. Code:
references/patterns.md
§ Snapshot testing.

适用场景: UI组件渲染输出、序列化数据结构、CLI格式化输出——这些场景下输出的精确结构很重要,且逐字段断言过于繁琐。
不适用场景: 频繁变更的输出(快照疲劳导致草率审核)、大型快照(难以审核)、实现细节(CSS类名、内部ID),或当只需断言某个特定值时替代精准断言。
对于小型输出(少于20行)优先使用内联快照,对于动态字段(如ID、时间戳)使用属性匹配器
expect.any(String)
)。CI运行时务必添加
--ci
参数,这样未知快照会导致失败而非被静默写入并提交。代码示例:
references/patterns.md
的快照测试章节。

Anti-Patterns

反模式

Testing private methods — Test through the public API. If a private method really needs its own tests, extract it to its own module with a public surface.
Mocking everything — Only mock external boundaries (network, filesystem, DB, time). A suite where every collaborator is mocked passes while the wiring between them is broken.
The plural
coverageThresholds
— Jest ignores it; the gate never fires; CI is green at any coverage. The key is
coverageThreshold
(singular). See Coverage above.
Faking all timers blindly
jest.useFakeTimers()
/
vi.useFakeTimers()
with no allowlist can deadlock code awaiting a real microtask. Fake only what the test needs (
doNotFake
/
toFake
). See
references/patterns.md
§ Jest timers.
Async test without
await
— a forgotten
await
makes the assertion never run and the test passes vacuously. Add
expect.assertions(n)
/
expect.hasAssertions()
to async tests so a missing assertion fails them.
Snapshot overuse — Use
expect(x).toBe("active")
for a specific value; reserve snapshots for structured output you can't assert field-by-field.
Non-descriptive names — Replace
"works"
with
"should return empty array when no items match the filter"
.
Shared mutable state — Initialize in
beforeEach
, not at module scope:
typescript
// Bad: shared mutation               // Good: fresh per test
const items = [];                     let items: string[];
it("A", () => items.push("a"));       beforeEach(() => { items = []; });
it("B", () => {                       it("A", () => { items.push("a"); expect(items).toHaveLength(1); });
  items.push("b");                    it("B", () => { items.push("b"); expect(items).toHaveLength(1); });
  expect(items).toHaveLength(1); // FAILS
});

测试私有方法——通过公共API进行测试。如果私有方法确实需要单独测试,将其提取为独立模块并暴露公共接口。
Mock所有对象——仅mock外部边界(网络、文件系统、数据库、时间)。如果测试套件将所有协作对象都mock掉,即使它们之间的连接逻辑损坏,测试仍会通过。
使用复数形式
coverageThresholds
——Jest会忽略该配置,门禁永远不会生效,CI在任何覆盖率下都会显示绿色。正确的配置项是
coverageThreshold
(单数形式)。详见上文覆盖率配置部分。
盲目伪造所有计时器——不带白名单的
jest.useFakeTimers()
/
vi.useFakeTimers()
可能导致等待真实微任务的代码死锁。仅伪造测试所需的计时器(使用
doNotFake
/
toFake
配置)。详见
references/patterns.md
的Jest计时器章节。
异步测试未添加
await
——遗漏
await
会导致断言永远不会执行,测试无意义地通过。在异步测试中添加
expect.assertions(n)
/
expect.hasAssertions()
,确保遗漏断言时测试失败。
过度使用快照——对于特定值,使用
expect(x).toBe("active")
这类精准断言;仅在无法逐字段断言结构化输出时使用快照。
测试名称无描述性——将
"works"
替换为
"当没有匹配项时应返回空数组"
这类明确名称。
共享可变状态——在
beforeEach
中初始化状态,而非模块作用域:
typescript
// 不良示例:共享可变状态               // 良好示例:每个测试使用全新状态
const items = [];                     let items: string[];
it("A", () => items.push("a"));       beforeEach(() => { items = []; });
it("B", () => {                       it("A", () => { items.push("a"); expect(items).toHaveLength(1); });
  items.push("b");                    it("B", () => { items.push("b"); expect(items).toHaveLength(1); });
  expect(items).toHaveLength(1); // 测试失败
});

Verification

验证步骤

Prove the suite runs and the gate actually fails on under-coverage — the exact thing the
coverageThreshold
typo silently disables.
  1. Tests run and pass:
    npx jest
    (or
    vitest run
    ,
    pytest -q
    ) exits
    0
    .
  2. The gate bites. Run coverage and confirm a non-zero exit when below threshold:
    bash
    npx jest --coverage --ci          # Jest/Vitest exit !=0 below coverageThreshold
    vitest run --coverage             # same for Vitest
    pytest --cov=src --cov-fail-under=80   # pytest exits !=0 below the floor
    Temporarily set a threshold above current coverage (e.g. 99) and confirm the command fails. If it exits
    0
    , your threshold key is wrong (likely the plural
    coverageThresholds
    ).
  3. Snapshots are safe in CI: the run uses
    --ci
    , so an unknown snapshot fails rather than being written.
    git status
    shows no new
    *.snap
    after a CI-mode run.

验证测试套件能正常运行,且门禁在覆盖率不足时确实会触发——这正是
coverageThreshold
拼写错误会静默失效的场景。
  1. 测试正常运行并通过:
    npx jest
    (或
    vitest run
    pytest -q
    )返回
    0
  2. 门禁生效。 运行覆盖率检查,确认未达到阈值时返回非零退出码:
    bash
    npx jest --coverage --ci          # Jest/Vitest在未达到coverageThreshold时返回非零值
    vitest run --coverage             # Vitest同理
    pytest --cov=src --cov-fail-under=80   # pytest在低于阈值时返回非零值
    临时将阈值设置为高于当前覆盖率(如99),确认命令会失败。如果仍返回
    0
    ,说明你的阈值配置项有误(大概率是使用了复数形式
    coverageThresholds
    )。
  3. CI中快照安全: 运行时使用
    --ci
    参数,这样未知快照会导致失败而非被自动写入。CI模式运行后,
    git status
    应显示无新的
    *.snap
    文件。

Done When

完成标准

  • Coverage thresholds configured in
    jest.config.*
    (key
    coverageThreshold
    , singular),
    vitest.config.*
    (
    coverage.thresholds
    ), or
    pyproject.toml
    (
    fail_under
    ) AND verified to exit non-zero below threshold (Verification step 2)
  • Test files all live in the project's single chosen location (co-located OR
    __tests__
    /
    tests/
    ) —
    git ls-files
    shows no ad-hoc test paths
  • External boundaries (HTTP, DB, time) are mocked and internal collaborators are not —
    grep
    finds no real network/DB clients constructed in test files
  • No test reaches outside the process boundary — suite passes with the network disabled and no test DB running
  • CI runs the test command with
    --ci
    (Jest/Vitest) so an unknown snapshot fails the build instead of being auto-written
  • 已在
    jest.config.*
    (配置项为
    coverageThreshold
    ,单数)、
    vitest.config.*
    coverage.thresholds
    )或
    pyproject.toml
    fail_under
    )中配置覆盖率阈值,并验证未达到阈值时会返回非零退出码(验证步骤2)
  • 所有测试文件均存于项目选定的单一位置(与源码同目录或
    __tests__
    /
    tests/
    目录)——
    git ls-files
    显示无临时测试路径
  • 外部边界(HTTP、数据库、时间)已被mock,内部协作对象未被mock——
    grep
    未在测试文件中找到真实网络/数据库客户端的实例化代码
  • 测试未超出进程边界——禁用网络且无测试数据库运行时,测试套件仍能通过
  • CI运行测试命令时添加了
    --ci
    参数(Jest/Vitest),这样未知快照会导致构建失败而非被自动写入

Reference Files (in
references/
)

参考文件(位于
references/
目录)

  • patterns.md — full runnable examples per framework: Jest setup/teardown, module/spy/timer mocks, async guards; Vitest config, in-source tests, concurrency, browser mode; pytest fixtures/parametrize/monkeypatch; Bun/Deno; the four test doubles; snapshot file/inline/property matchers.
  • patterns.md——各框架的完整可运行示例:Jest初始化/清理、模块/spy/计时器mock、异步防护;Vitest配置、源码内测试、并发、浏览器模式;pytest fixtures/参数化/monkeypatch;Bun/Deno;四种测试替身;快照文件/内联/属性匹配器。

Related Skills

相关技能

  • coverage-analysis — interpreting coverage reports, finding meaningful gaps, mutation score as a first-class signal. Go there to read coverage; stay here to configure and gate it.
  • ci-cd-integration — test stages in pipelines, parallelization, caching, deployment gating.
  • ai-test-generation — when an AI writes the test code from a spec/PRD; this skill is for writing and structuring tests by hand.
  • ai-qa-review — auditing existing tests for hallucinated APIs, fabricated imports, and closed-loop tests.
  • shift-left-testing — pre-commit hooks, IDE integration, and TDD workflow around these tests.
  • coverage-analysis——解读覆盖率报告、查找有意义的覆盖缺口、将突变分数作为核心指标。如需分析覆盖率,请使用该技能;本技能专注于覆盖率配置与门禁设置。
  • ci-cd-integration——流水线中的测试阶段、并行化、缓存、部署门禁。
  • ai-test-generation——由AI根据需求文档/PRD生成测试代码;本技能专注于手动编写和结构化测试。
  • ai-qa-review——审核现有测试中的API幻觉、伪造导入和闭环测试问题。
  • shift-left-testing——围绕这些测试的预提交钩子、IDE集成和TDD工作流。