你写的代码没有测试,就像出门不锁门——Jest + Testing Library 从入门到不慌

你改了一行代码,手动点了一遍页面,觉得没问题就上线了。结果用户反馈"登录按钮点不动了"。你心里咯噔:我根本没改登录相关代码啊。今天我们来给你的代码装一把"智能门锁"------单元测试。用 Jest + Testing Library,把常见 Bug 锁在门外,让你改代码时不再心惊胆战。

前言

很多前端对测试的态度是:项目那么赶,哪有时间写测试?结果修 Bug 的时间比写代码还多。你花 20 分钟写的测试,可能帮你省掉 2 小时的通宵排查。

测试不是"额外工作",而是安全网。当你需要重构、升级依赖、添加新功能时,测试全绿的那一刻,比中彩票还安心。今天我们用 Jest(测试框架)+ Testing Library(渲染组件、模拟用户操作),从零开始给你的 React 项目写第一个测试。不搞复杂概念,只写最实用的断言。

一、Jest 是啥?Testing Library 又是啥?

  • Jest:Facebook 出的测试框架,内置断言、模拟函数、覆盖率报告。开箱即用,零配置。
  • Testing Library:一套帮助你"像用户一样测试"的工具。不测试组件内部 state 或 props,只测试用户能看到和能操作的。

核心原则:测试越接近用户的使用方式,越能给你信心。不要测试实现细节(比如某个函数被调用了几次、某个 state 变了),要测试 UI 上出现了什么、点击后发生了什么变化。

二、环境搭建(Create React App 用户)

如果你用 CRA,Jest 和 Testing Library 已经内置,直接写就行。Vite 用户需要手动安装:

bash 复制代码
npm install -D jest @testing-library/react @testing-library/jest-dom @testing-library/user-event vitest
# 如果用 Vitest(Vite 推荐),配置略不同。这里我们用 Jest 示范

配置 jest.config.js

js 复制代码
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
};

src/setupTests.js

js 复制代码
import '@testing-library/jest-dom';

三、第一个测试:测试一个纯函数

测试最简单的工具函数,是入门的绝佳方式。比如 utils/formatPrice.js

js 复制代码
export function formatPrice(price, currency = '¥') {
  return `${currency}${price.toFixed(2)}`;
}

写测试 utils/formatPrice.test.js

js 复制代码
import { formatPrice } from './formatPrice';

test('格式化价格带默认货币符号', () => {
  expect(formatPrice(10.5)).toBe('¥10.50');
});

test('支持自定义货币符号', () => {
  expect(formatPrice(10.5, '$')).toBe('$10.50');
});

运行 npm test,看到绿色通过。这类测试跑得快,你应该写很多。

四、测试 React 组件:渲染与交互

假设我们有一个 Counter 组件:

jsx 复制代码
import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

写测试 Counter.test.jsx

jsx 复制代码
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Counter } from './Counter';

test('渲染初始计数为0', () => {
  render(<Counter />);
  const countElement = screen.getByText(/计数: 0/i);
  expect(countElement).toBeInTheDocument();
});

test('点击按钮后计数增加', async () => {
  const user = userEvent.setup();
  render(<Counter />);
  const button = screen.getByRole('button', { name: /增加/i });
  await user.click(button);
  expect(screen.getByText(/计数: 1/i)).toBeInTheDocument();
});

注意:

  • screen.getByRolegetByText 更语义化,推荐优先使用。
  • userEvent 模拟真实点击(会触发 focus、blur 等),比 fireEvent 更接近用户。

五、测试异步操作:比如数据加载

一个显示用户列表的组件,从 API 获取数据:

jsx 复制代码
import { useEffect, useState } from 'react';

export function UserList() {
  const [users, setUsers] = useState([]);
  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(setUsers);
  }, []);
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

测试时需要 mock fetch

jsx 复制代码
import { render, screen, waitFor } from '@testing-library/react';
import { UserList } from './UserList';

global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve([{ id: 1, name: '张三' }, { id: 2, name: '李四' }]),
  })
);

test('加载并显示用户列表', async () => {
  render(<UserList />);
  // 等待数据加载完成
  await waitFor(() => {
    expect(screen.getByText('张三')).toBeInTheDocument();
    expect(screen.getByText('李四')).toBeInTheDocument();
  });
});

六、覆盖率:别盲目追求 100%

运行 npm test -- --coverage,会生成覆盖率报告。但记住:100% 覆盖率不代表没有 Bug。覆盖率低的地方可能是关键逻辑,需要补测试;但有些样板代码(如常量定义、简单 getter)不测也罢。重点覆盖业务逻辑和复杂交互。

七、测试最佳实践

  • 测试行为,不测试实现:不要测试组件内部 state 的值(除非必要),而是测试渲染结果。
  • 一个测试只断言一件事 :一个 test 里可以有多个 expect,但最好只测一个行为。
  • 模拟外部依赖:网络请求、localStorage、计时器都要模拟,避免测试不稳定。
  • 避免测试快照 :快照测试(toMatchSnapshot)容易产生大而脆弱的文件,改个空格就挂。优先用断言。
  • 让测试快速:单元测试应该在几秒内跑完,如果慢,检查是否有真实网络请求或大量渲染。

八、持续集成:让测试自动跑起来

把测试放到 GitHub Actions 里(上篇文章的内容)。每次 PR 自动跑测试,不通过不让合并。这样团队协作时,队友的改动不会悄悄破坏你的代码。

yaml 复制代码
name: Test
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 18
      - run: npm ci
      - run: npm test

九、总结:测试是给未来的自己写信

  • 写测试一开始会慢,但能让你后期"闭着眼睛改代码"。
  • Jest + Testing Library 是 React 社区标准,Vue/Vite 对应 Vitest + Testing Library。
  • 不要被"测试种类太多"吓到,从纯函数和简单组件开始,逐步扩大覆盖。

下次你改了代码,测试全绿,你就可以自信地 push。那种感觉,比手动点一百遍页面踏实多了。

如果你觉得今天的"智能门锁"够踏实,点个赞让更多人看到。评论区聊聊:你被上线后突然出现的 Bug 坑过吗?

相关推荐
yuzhiboyouye3 小时前
web前端英语面试
前端·面试·状态模式
canonical_entropy4 小时前
下一代低代码渲染框架 nop-chaos-flux 的设计原则
前端·低代码·前端框架
东方小月4 小时前
5分钟搞懂Harness Engineering(驾驭工程):从提示词到AI Agent的进化之路
前端·后端·架构
我叫黑大帅4 小时前
为什么需要 @types/react?解决“无法找到模块 react 的声明文件”报错
前端·javascript·面试
之歆4 小时前
DAY_21JavaScript 深度解析:数组(Array)与函数(Function)(一)
前端·javascript
XinZong5 小时前
【AI社交】基于OpenClaw自研轻量化AI社交平台实战
前端
Le_ee5 小时前
ctfweb:php/php短标签/.haccess+图片马/XXE
开发语言·前端·php
爱上好庆祝5 小时前
学习js的第七天(wed APIs的开始)
前端·javascript·css·学习·html·css3
KaMeidebaby6 小时前
卡梅德生物技术快报|冻干工艺开发:注射用心肌肽全流程参数优化与工程化方案
前端·其他·百度·新浪微博