测试基础指南 testing-fundamentals

本技能提供软件测试的基础策略指南,包括单元测试、集成测试和端到端测试,使用Vitest、Jest、Playwright等框架,覆盖测试金字塔、AAA模式、常见错误和最佳实践,帮助开发者编写高质量测试代码,适用于测试覆盖率、模拟策略和测试框架选择。

测试 0 次安装 0 次浏览 更新于 3/8/2026

name: testing-fundamentals description: 指导单元测试、集成测试和端到端测试的策略。用于当新手问“如何测试”、“写测试”、“应该测试什么”、“测试覆盖率”、“模拟”,或使用Vitest、Jest、Playwright时。提供测试金字塔和AAA模式。

测试基础回顾

“如果你不能测试它,你就没有理解它。测试是理解的证明。”

何时应用

在以下情况激活此技能:

  • 审查测试文件(*.test.ts, *.spec.ts, *.test.js)
  • 新手询问测试策略
  • 完成一个没有测试的功能
  • 讨论覆盖率或测试质量

测试金字塔

        ▲
       ╱ ╲     端到端测试 (10%)
      ╱   ╲    Playwright - 完整用户流程
     ╱─────╲
    ╱       ╲  集成测试 (20%)
   ╱         ╲ Vitest + RTL - 组件交互
  ╱───────────╲
 ╱             ╲ 单元测试 (70%)
╱               ╲ Vitest - 函数、工具、逻辑
─────────────────
  • 单元测试 (70%): 快速、隔离、测试一件事
  • 集成测试 (20%): 组件一起工作
  • 端到端测试 (10%): 仅关键用户旅程

堆栈特定框架指南

堆栈 单元/集成 端到端
Vite + React Vitest + React Testing Library Playwright
Create React App Jest + RTL Playwright
Next.js Vitest 或 Jest + RTL Playwright
Node.js Vitest (原生ESM) -
Python pytest -
Go go test -

为什么在Vite中使用Vitest?

  • 在监视模式下比Jest快10-20倍
  • 原生ESM支持
  • 与Vite相同配置(vite.config.ts)
  • 兼容Jest API

测试什么(三个问题)

  1. 快乐路径: 当一切正常时,它是否工作?
  2. 边界情况: 空值、null、最大值时会发生什么?
  3. 错误状态: 它是否优雅地失败?

好的测试检查:

  • [ ] 组件渲染不崩溃
  • [ ] 用户交互工作(点击、输入、提交)
  • [ ] 错误状态正确显示
  • [ ] 加载状态出现和消失
  • [ ] 数据正确流经组件

坏的测试检查:

  • [ ] 实现细节(内部状态、方法调用)
  • [ ] 样式或CSS类
  • [ ] 第三方库内部
  • [ ] 大组件的快照测试(脆弱)

常见错误(反模式)

1. 测试实现,而非行为

// ❌ 错误:测试内部状态
expect(component.state.isLoading).toBe(true);

// ✅ 正确:测试用户看到的
expect(screen.getByText('加载中...')).toBeInTheDocument();

2. 过度模拟

// ❌ 错误:模拟一切
jest.mock('./utils');
jest.mock('./api');
jest.mock('./hooks');
// 此时你甚至测试什么?

// ✅ 正确:仅模拟外部边界
vi.mock('./api'); // 模拟API,测试其余部分

3. 测试第三方库

// ❌ 错误:测试React Query是否工作
expect(useQuery).toHaveBeenCalledWith('users');

// ✅ 正确:测试你的代码行为
await waitFor(() => {
  expect(screen.getByText('用户名')).toBeInTheDocument();
});

4. 脆弱的选择器

// ❌ 错误:如果更改CSS会中断
screen.getByClassName('btn-primary-large-blue');

// ✅ 正确:语义化和稳定
screen.getByRole('button', { name: '提交' });

5. 测试一切

// ❌ 错误:100%覆盖率目标
// 导致仅为了覆盖率而存在的测试

// ✅ 正确:战略性覆盖率
// 测试关键路径、边界情况、复杂逻辑

苏格拉底式问题

问这些而不是给出答案:

  1. 策略: “最需要测试的关键用户流程是什么?”
  2. 覆盖率: “如果这个测试通过但功能坏了,你怎么知道?”
  3. 边界情况: “什么输入可能破坏这个?空?null?10,000个项目?”
  4. 隔离: “你是在测试你的代码还是库的代码?”
  5. 价值: “这个测试会捕获真正的错误吗?”

测试结构(AAA模式)

describe('登录表单', () => {
  it('当密码太短时显示错误', async () => {
    // 安排
    render(<LoginForm />);

    // 行动
    await userEvent.type(screen.getByLabelText('密码'), '123');
    await userEvent.click(screen.getByRole('button', { name: '登录' }));

    // 断言
    expect(screen.getByText('密码必须至少8个字符')).toBeInTheDocument();
  });
});

需要指出的红旗

红旗 问题
功能没有测试 “什么测试证明这个工作?”
只测试了快乐路径 “如果API失败呢?如果输入为空呢?”
模拟一切 “你实际上在这里测试什么?”
测试实现 “如果你重构但行为保持不变,这个测试会中断吗?”
使用getByClassName “有更语义化的方式选择这个元素吗?”
大快照测试 “当它改变时,你真的会审查这个差异吗?”

MCP 使用

Context7 - 框架文档

获取:Vitest 文档
获取:React Testing Library 查询
获取:Playwright 最佳实践

Octocode - 真实示例

搜索:流行仓库中的“vitest react testing library”
搜索:E2E模式的“playwright e2e test login”

测试命名约定

// 模式:it('应该 [预期行为] 当 [条件]')

it('当密码无效时应该显示错误消息')
it('当登录成功时应该重定向到仪表板')
it('当表单正在提交时应该禁用提交按钮')

面试黄金

“我实施了全面的测试,覆盖85%的关键用户流程。我使用Vitest进行单元测试,React Testing Library进行组件集成测试,Playwright进行端到端测试,覆盖登录、结账和支付流程。”

测试是面试的谈资。你写的每个测试都是你理解代码的证明。