writing-tests

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Writing Tests

编写测试

Use this skill when the user asks to add tests to existing code, improve test coverage, or write tests for a specific file or module.
当用户要求为现有代码添加测试、提升测试覆盖率或为特定文件/模块编写测试时,使用此技能。

Steps

步骤

  1. Detect the test setup — check what's already configured:
    bash
    # Check package.json for test runner
    cat package.json | grep -E "jest|vitest|mocha|playwright|cypress"
    Look for config files:
    vitest.config.ts
    ,
    jest.config.ts
    ,
    playwright.config.ts
    ,
    .mocharc.*
    . Check for existing test files to understand the project's test patterns and conventions.
  2. If no test runner exists — set one up:
    bash
    npm install -D vitest @testing-library/react @testing-library/jest-dom
    Add a
    test
    script to
    package.json
    :
    json
    { "test": "vitest run", "test:watch": "vitest" }
  3. Analyze the target code — read the file(s) to test and identify:
    • Public API: exported functions, classes, components, hooks
    • Code paths: conditionals, error handling, edge cases
    • Dependencies: external services, databases, APIs that need mocking
    • Side effects: file I/O, network calls, DOM mutations
  4. Create the test file — place it next to the source file or in a
    __tests__/
    directory, matching the project's convention:
    • src/utils/format.ts
      src/utils/format.test.ts
    • src/components/Button.tsx
      src/components/Button.test.tsx
  5. Write tests following this structure:
    ts
    import { describe, it, expect, vi } from "vitest";
    
    describe("functionName", () => {
      // Happy path
      it("returns formatted output for valid input", () => { ... });
    
      // Edge cases
      it("handles empty string", () => { ... });
      it("handles null/undefined input", () => { ... });
    
      // Error cases
      it("throws on invalid argument", () => { ... });
    
      // Boundary conditions
      it("handles maximum length input", () => { ... });
    });
  6. Mock external dependencies — don't make real API calls or database queries in unit tests:
    ts
    vi.mock("@/lib/db", () => ({
      query: vi.fn().mockResolvedValue([{ id: 1, name: "test" }]),
    }));
    For React components, mock hooks that fetch data:
    ts
    vi.mock("@/hooks/useUser", () => ({
      useUser: () => ({ user: { name: "Test" }, isLoading: false }),
    }));
  7. Test React components with Testing Library:
    tsx
    import { render, screen, fireEvent } from "@testing-library/react";
    
    it("renders the button and handles click", () => {
      const onClick = vi.fn();
      render(<Button onClick={onClick}>Click me</Button>);
      fireEvent.click(screen.getByRole("button", { name: "Click me" }));
      expect(onClick).toHaveBeenCalledOnce();
    });
  8. Run the tests and verify they pass:
    bash
    npm test
    If any fail, fix the test or the code (depending on whether the test expectation or the implementation is wrong).
  1. 检测测试配置 — 检查已有的配置:
    bash
    # Check package.json for test runner
    cat package.json | grep -E "jest|vitest|mocha|playwright|cypress"
    查找配置文件:
    vitest.config.ts
    jest.config.ts
    playwright.config.ts
    .mocharc.*
    。查看现有测试文件以了解项目的测试模式和约定。
  2. 如果没有测试运行器 — 配置一个
    bash
    npm install -D vitest @testing-library/react @testing-library/jest-dom
    package.json
    中添加
    test
    脚本:
    json
    { "test": "vitest run", "test:watch": "vitest" }
  3. 分析目标代码 — 读取要测试的文件并识别:
    • 公共API:导出的函数、类、组件、hooks
    • 代码路径:条件分支、错误处理、边界情况
    • 依赖项:需要模拟的外部服务、数据库、API
    • 副作用:文件I/O、网络请求、DOM变更
  4. 创建测试文件 — 遵循项目约定,将其放在源文件旁边或
    __tests__/
    目录中:
    • src/utils/format.ts
      src/utils/format.test.ts
    • src/components/Button.tsx
      src/components/Button.test.tsx
  5. 按照以下结构编写测试
    ts
    import { describe, it, expect, vi } from "vitest";
    
    describe("functionName", () => {
      // Happy path
      it("returns formatted output for valid input", () => { ... });
    
      // Edge cases
      it("handles empty string", () => { ... });
      it("handles null/undefined input", () => { ... });
    
      // Error cases
      it("throws on invalid argument", () => { ... });
    
      // Boundary conditions
      it("handles maximum length input", () => { ... });
    });
  6. 模拟外部依赖项 — 单元测试中不要发起真实的API请求或数据库查询:
    ts
    vi.mock("@/lib/db", () => ({
      query: vi.fn().mockResolvedValue([{ id: 1, name: "test" }]),
    }));
    对于React组件,模拟获取数据的hooks:
    ts
    vi.mock("@/hooks/useUser", () => ({
      useUser: () => ({ user: { name: "Test" }, isLoading: false }),
    }));
  7. 使用Testing Library测试React组件
    tsx
    import { render, screen, fireEvent } from "@testing-library/react";
    
    it("renders the button and handles click", () => {
      const onClick = vi.fn();
      render(<Button onClick={onClick}>Click me</Button>);
      fireEvent.click(screen.getByRole("button", { name: "Click me" }));
      expect(onClick).toHaveBeenCalledOnce();
    });
  8. 运行测试并验证是否通过:
    bash
    npm test
    如果有测试失败,修复测试或代码(取决于测试预期还是实现存在问题)。

What to Test

测试内容

  • Always test: public API, error handling, edge cases (empty, null, zero, negative), state transitions, async behavior
  • Skip testing: private implementation details, third-party library internals, simple getters/setters, type-only code
  • 必须测试:公共API、错误处理、边界情况(空值、null、零、负数)、状态转换、异步行为
  • 无需测试:私有实现细节、第三方库内部逻辑、简单的getter/setter、仅类型代码

Notes

注意事项

  • Match the project's existing test style — if they use
    test()
    instead of
    it()
    , follow that.
  • Don't test implementation details — test behavior and outputs, not internal method calls.
  • Use descriptive test names that explain the scenario: "returns 0 when cart is empty" not "test1".
  • One assertion concept per test — multiple
    expect
    calls are fine if they verify the same behavior.
  • For async code, always
    await
    the result or use
    resolves
    /
    rejects
    matchers.
  • 匹配项目现有的测试风格 — 如果项目使用
    test()
    而非
    it()
    ,遵循该风格。
  • 不要测试实现细节 — 测试行为和输出,而非内部方法调用。
  • 使用描述性的测试名称来解释场景:比如“购物车为空时返回0”而非“test1”。
  • 每个测试对应一个断言概念 — 如果是验证同一行为,多个
    expect
    调用是可行的。
  • 对于异步代码,务必
    await
    结果或使用
    resolves
    /
    rejects
    匹配器。