accelint-ts-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVitest Best Practices
Vitest最佳实践
Comprehensive patterns for writing maintainable, effective vitest tests. Focused on expert-level guidance for test organization, clarity, and performance.
本文档提供编写可维护、高效Vitest测试的全面模式,聚焦于测试组织、清晰度和性能方面的专家级指导。
NEVER Do When Writing Vitest Tests
编写Vitest测试时绝对不要做的事
- NEVER skip global mock cleanup configuration - Manual cleanup appears safe but creates "action at a distance" failures: a mock in test file A leaks into test file B running 3 files later, causing non-deterministic failures that only appear when tests run in specific orders. These Heisenbugs waste hours in CI debugging. Configure ,
clearMocks: true,resetMocks: trueinrestoreMocks: trueonce to eliminate this entire class of order-dependent failure.vitest.config.ts - NEVER nest describe blocks more than 2 levels deep - Deep nesting creates cognitive overhead and excessive indentation. Put context in test names instead: vs
it('should add item to empty cart').describe('when cart is empty', () => describe('addItem', ...)) - NEVER mock your own pure functions - Mocking internal code makes tests brittle and less valuable. Mock only external dependencies (APIs, databases, third-party libraries). Prefer fakes > stubs > spies > mocks.
- NEVER use loose assertions like or
toBeTruthy()- These assertions pass for multiple distinct values you never intended:toBeDefined()passes fortoBeTruthy(),1,"false", and[]- all semantically different. When refactoring changes{}from returninggetUser()to returning{id: 1}, your test still passes but your production code breaks. Loose assertions create false confidence that evaporates in production.1is NOT a loose assertion.toBeTypeOf() - NEVER test implementation details instead of behavior - Tests that verify "function X was called 3 times" create false failures: you optimize code to call X once via memoization, all tests fail, yet the user experience is identical (and faster). These tests actively punish performance improvements and refactoring. Test what users observe (outputs given inputs), not how your code achieves it internally.
- NEVER share mutable state between tests - Tests that depend on execution order or previous test state create flaky, unreliable suites. Each test must be fully independent with fresh setup.
- NEVER use or skip type checking in test files - When implementation signatures change, tests with
anysilently pass while calling functions with wrong arguments. You ship broken code that TypeScript could have caught. Tests are executable documentation:as anycommunicates nothing, butuser as anyshows exactly what properties matter for this test case.createTestUser(Partial<User>)
- 绝对不要跳过全局Mock清理配置 - 手动清理看似安全,但会引发"远距离影响"的故障:测试文件A中的Mock会泄漏到后续运行的测试文件B中,导致仅在特定测试执行顺序下才会出现的非确定性故障。这类Heisenbugs会在CI调试中浪费大量时间。只需在中配置
vitest.config.ts、clearMocks: true、resetMocks: true一次,就能彻底消除这类依赖执行顺序的故障。restoreMocks: true - 绝对不要嵌套超过2层的describe块 - 深层嵌套会增加认知负担和过多缩进。应将上下文放在测试名称中:例如使用,而非
it('should add item to empty cart')。describe('when cart is empty', () => describe('addItem', ...)) - 绝对不要Mock自己的纯函数 - Mock内部代码会让测试变得脆弱且价值降低。仅Mock外部依赖(如API、数据库、第三方库)。优先使用fakes > stubs > spies > mocks。
- 绝对不要使用或
toBeTruthy()这类松散断言 - 这些断言会意外匹配多个不同的值:toBeDefined()会匹配toBeTruthy()、1、"false"和[]——这些在语义上完全不同。当重构将{}的返回值从getUser()改为{id: 1}时,测试仍会通过,但生产代码已出现问题。松散断言会营造虚假的安全感,在生产环境中荡然无存。1不属于松散断言。toBeTypeOf() - 绝对不要测试实现细节而非行为 - 验证"函数X被调用了3次"的测试会引发虚假失败:当你通过 memoization 优化代码,将函数X的调用次数减少到1次时,所有测试都会失败,但用户体验完全相同(甚至更快)。这类测试会阻碍性能优化和重构。应测试用户能观察到的内容(输入→输出),而非代码内部的实现方式。
- 绝对不要在测试之间共享可变状态 - 依赖执行顺序或前置测试状态的测试会导致测试套件不稳定、不可靠。每个测试必须完全独立,拥有全新的初始化环境。
- 绝对不要在测试文件中使用或跳过类型检查 - 当实现签名变更时,使用
any的测试会静默通过,但调用函数时传入的参数已错误。你会发布TypeScript本可以捕获的有问题代码。测试是可执行的文档:as any毫无意义,但user as any能清晰展示该测试用例关注哪些属性。createTestUser(Partial<User>)
Before Writing Tests, Ask
编写测试前先思考这些问题
Apply these expert thinking patterns before implementing tests:
在实现测试前,先应用这些专家级思考模式:
Test Isolation and Setup
测试隔离与初始化
- Where should cleanup logic live? Think in layers: configuration eliminates entire error classes (mock cleanup in vitest.config.ts), setup files handle project-wide concerns (custom matchers, global mocks), beforeEach handles test-specific state. Each test doing its own mock cleanup is like each function doing its own null checks - it works but misses the point. Push concerns to the highest appropriate layer.
- Does this test depend on previous tests or shared state? Test suites are parallel universes - each test should work identically whether it runs first, last, or alone. State dependency creates "quantum tests" that pass or fail based on execution order. If a test needs data from another test, they're actually one test split artificially.
- 清理逻辑应该放在哪里? 按层级思考:配置可消除整类错误(如在vitest.config.ts中配置Mock清理),初始化文件处理项目级别的通用配置(如自定义匹配器、全局Mock),beforeEach处理测试专属的状态。每个测试都自行处理Mock清理,就像每个函数都自行做空值检查——虽然可行,但没抓住核心。应将关注点推到最高的合适层级。
- 该测试是否依赖前置测试或共享状态? 测试套件是并行独立的——每个测试无论先运行、后运行还是单独运行,都应表现一致。状态依赖会导致"量子测试",其成败取决于执行顺序。如果一个测试需要另一个测试的数据,那它们实际上是一个被人为拆分的测试。
What to Test
测试内容的选择
- Am I testing behavior or implementation? Test what users experience (inputs → outputs), not how code achieves it (which functions were called). Implementation tests break during safe refactoring.
- What's the simplest dependency I can use? Real implementation > fake > stub > spy > mock. Each step down this hierarchy adds brittleness. Mock only when using real code is impractical (external APIs, slow operations).
- 我是在测试行为还是实现细节? 应测试用户体验到的内容(输入→输出),而非代码的实现方式(如哪些函数被调用)。测试实现细节的测试会在安全重构时失败。
- 我可以使用的最简单依赖是什么? 优先顺序为:真实实现 > fake > stub > spy > mock。每向下一层,测试的脆弱性都会增加。仅当使用真实代码不切实际时(如外部API、慢速操作)才使用Mock。
Test Clarity
测试清晰度
- Can someone understand this test in 5 seconds? Follow AAA pattern (Arrange, Act, Assert) with clear boundaries. If setup is complex, extract to helper functions with descriptive names.
- Are there multiple variations of the same behavior? Use for parameterized tests instead of copying test structure. One assertion per concept keeps tests focused.
it.each()
- 别人能在5秒内理解这个测试吗? 遵循AAA模式(Arrange、Act、Assert),并保持清晰的边界。如果初始化逻辑复杂,可提取到具有描述性名称的辅助函数中。
- 是否存在同一行为的多个变体? 使用实现参数化测试,而非复制测试结构。每个测试应聚焦一个概念,对应一个断言。
it.each()
Performance and Maintenance
性能与可维护性
- Will this test still be valuable in 6 months? Avoid testing framework internals or trivial operations. Focus on business logic, edge cases, and error handling that actually prevent bugs.
- Is this test fast enough to run on every save? Avoid expensive operations in tests. Use fakes for databases, mock timers for delays, stub external calls. Tests should complete in milliseconds.
- 这个测试在6个月后仍有价值吗? 避免测试框架内部逻辑或琐碎操作。专注于业务逻辑、边缘情况和错误处理,这些才是真正能预防bug的内容。
- 这个测试的速度足够支持每次保存后运行吗? 避免在测试中执行昂贵操作。使用fake替代数据库,使用Mock定时器处理延迟,Stub外部调用。测试应在毫秒级完成。
What This Skill Covers
本文档涵盖的内容
Expert guidance on vitest testing patterns:
- Organization - File placement, naming, describe block structure
- AAA Pattern - Arrange, Act, Assert for instant clarity
- Parameterized Tests - Using to reduce duplication
it.each() - Error Handling - Testing exceptions, edge cases, fault injection
- Assertions - Strict assertions to catch unintended values
- Test Doubles - Fakes, stubs, mocks, spies hierarchy and when to use each
- Async Testing - Promises, async/await, timers, concurrent tests
- Performance - Fast tests through efficient setup and global config
- Vitest Features - Coverage, watch mode, setup files, config discovery
- Snapshot Testing - When snapshots help vs hurt maintainability
关于Vitest测试模式的专家级指导:
- 组织方式 - 文件放置、命名、describe块结构
- AAA模式 - Arrange、Act、Assert,实现即时清晰的测试结构
- 参数化测试 - 使用减少代码重复
it.each() - 错误处理 - 测试异常、边缘情况、故障注入
- 断言 - 使用严格断言捕获非预期值
- Test Doubles - Fakes、Stubs、Mocks、Spies的层级及适用场景
- 异步测试 - Promise、async/await、定时器、并发测试
- 性能优化 - 通过高效初始化和全局配置实现快速测试
- Vitest特性 - 覆盖率、监听模式、初始化文件、配置自动发现
- 快照测试 - 快照测试的适用场景与反模式
How to Use
使用方法
This skill uses a progressive disclosure structure to minimize context usage:
本文档采用渐进式披露结构,以最小化上下文依赖:
1. Start with the Overview (AGENTS.md)
1. 从概览开始(AGENTS.md)
Read AGENTS.md for a concise overview of all rules with one-line summaries and the workflow for discovering existing test configuration.
阅读AGENTS.md获取所有规则的简洁概览,包括单行摘要和发现现有测试配置的工作流。
2. Check for Existing Test Configuration
2. 检查现有测试配置
Before writing tests:
- First check for global mock cleanup settings (
vitest.config.ts,clearMocks,resetMocks)restoreMocks - Then search for setup files (,
test/setup.ts, etc.) and analyze their configurationvitest.setup.ts - See the workflow in AGENTS.md
编写测试前:
- 首先检查中的全局Mock清理设置(
vitest.config.ts、clearMocks、resetMocks)restoreMocks - 然后查找初始化文件(如、
test/setup.ts等)并分析其配置vitest.setup.ts - 参考AGENTS.md中的工作流
3. Load Specific Rules as Needed
3. 根据需要加载特定规则
Use these explicit triggers to know when to load each reference file:
MANDATORY Loading (load entire file):
- Writing async tests with promises/timers → async-testing.md
- Working with mocks, stubs, spies, or fakes → test-doubles.md
Load When You See These Patterns:
- Nested describe blocks >2 levels deep → organization.md
- Test files not co-located with implementation → organization.md
- Tests without clear Arrange/Act/Assert structure → aaa-pattern.md
- Duplicate test code with slight variations → parameterized-tests.md
- Missing error case tests or inadequate edge case coverage → error-handling.md
- Loose assertions like or
toBeTruthy()→ assertions.mdtoBeDefined() - Tests running slow (>100ms per test) → performance.md
- Need coverage, watch mode, or vitest-specific features → vitest-features.md
- Considering or reviewing snapshot tests → snapshot-testing.md
Do NOT Load Unless Specifically Needed:
- Do NOT load performance.md if tests are fast (<50ms)
- Do NOT load snapshot-testing.md unless snapshots are mentioned
- Do NOT load vitest-features.md for basic test writing
通过以下明确触发条件判断何时加载各参考文件:
强制加载(加载整个文件):
- 编写使用Promise/定时器的异步测试 → async-testing.md
- 使用Mock、Stub、Spy或Fake → test-doubles.md
出现以下模式时加载:
- 嵌套describe块超过2层 → organization.md
- 测试文件未与实现代码同目录放置 → organization.md
- 测试没有清晰的Arrange/Act/Assert结构 → aaa-pattern.md
- 存在仅略有差异的重复测试代码 → parameterized-tests.md
- 缺少错误场景测试或边缘情况覆盖不足 → error-handling.md
- 使用或
toBeTruthy()这类松散断言 → assertions.mdtoBeDefined() - 测试运行缓慢(单测试>100ms) → performance.md
- 需要使用覆盖率、监听模式或Vitest专属特性 → vitest-features.md
- 考虑使用或评审快照测试 → snapshot-testing.md
非必要时请勿加载:
- 如果测试速度快(<50ms),请勿加载performance.md
- 除非提及快照测试,否则请勿加载snapshot-testing.md
- 基础测试编写场景下,请勿加载vitest-features.md
4. Apply the Pattern
4. 应用对应模式
Each reference file contains:
- ❌ Incorrect examples showing the anti-pattern
- ✅ Correct examples showing the optimal implementation
- Explanations of why the pattern matters
每个参考文件包含:
- ❌ 展示反模式的错误示例
- ✅ 展示最优实现的正确示例
- 该模式重要性的解释
5. Use the Report Template
5. 使用报告模板
When this skill is invoked for test code review, use the standardized report format:
Template:
assets/output-report-template.mdThe report format provides:
- Executive Summary with test quality impact assessment
- Severity levels (Critical, High, Medium, Low) for prioritization
- Impact analysis (test reliability, maintainability, performance, clarity)
- Categorization (Test Organization, Assertions, Test Doubles, Async Testing, Performance)
- Pattern references linking to detailed guidance in references/
- Summary table for tracking all issues
When to use the report template:
- Skill invoked directly via
/accelint-ts-testing <path> - User asks to "review test code" or "audit tests" across file(s), invoking skill implicitly
When NOT to use the report template:
- User asks to "write a test for this function" (direct implementation)
- User asks "what's wrong with this test?" (answer the question)
- User requests specific test fixes (apply fixes directly without formal report)
当本文档用于测试代码评审时,使用标准化报告格式:
模板:
assets/output-report-template.md报告格式包含:
- 测试质量影响评估的执行摘要
- 用于优先级排序的严重级别(Critical、High、Medium、Low)
- 影响分析(测试可靠性、可维护性、性能、清晰度)
- 分类(测试组织、断言、Test Doubles、异步测试、性能)
- 指向references/中详细指导的模式链接
- 用于跟踪所有问题的汇总表格
何时使用报告模板:
- 通过直接调用本文档
/accelint-ts-testing <path> - 用户要求“评审测试代码”或“审计测试”(跨文件),隐式触发本文档
何时不使用报告模板:
- 用户要求“为这个函数编写测试”(直接实现)
- 用户询问“这个测试有什么问题?”(直接回答问题)
- 用户请求特定的测试修复(直接应用修复,无需正式报告)
Quick Example
快速示例
See quick-start.md for a complete before/after example showing how this skill transforms unclear tests into clear, maintainable ones.
查看quick-start.md获取完整的前后对比示例,了解本文档如何将模糊的测试转换为清晰、可维护的测试。