React 项目 Jest 测试实践与常用 API

55 阅读2分钟

一、Jest 配置

1. 项目依赖配置

1.1 依赖管理

{
  "dependencies": {
    "jest": "^30.0.5",                        // Jest 测试框架
    "ts-jest": "^29.4.1",                     // TypeScript 支持
    "@types/jest": "^30.0.0"                  // Jest TypeScript 类型
  },
  "devDependencies": {
    "@testing-library/jest-dom": "^6.7.0",    // DOM 测试增强工具
    "@testing-library/react": "^13.4.0",      // React 测试工具
    "entities": "^6.0.1",                     // HTML 实体解析
    "identity-obj-proxy": "^3.0.0"            // 样式文件模拟
  }
}

1.2 Jest 配置 (package.json)

{
  "jest": {
    "testEnvironment": "jsdom",               // 使用 jsdom 环境模拟浏览器
    "setupFilesAfterEnv": [
      "<rootDir>/setupTests.js"               // 测试环境设置文件
    ],
    "moduleNameMapper": {
      "\\\\.(css|less)$": "identity-obj-proxy"  // 处理样式文件
    },
    "coverageDirectory": "coverage",          // 测试覆盖率报告目录
    "collectCoverage": true                   // 启用覆盖率收集
  }
}

2. 环境配置

2.1 设置测试环境 (setupTests.js)

// 导入 DOM 测试工具
import '@testing-library/jest-dom';

// 模拟 window.matchMedia
Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

// 模拟 ResizeObserver
global.ResizeObserver = jest.fn().mockImplementation(() => ({
  observe: jest.fn(),
  unobserve: jest.fn(),
  disconnect: jest.fn(),
}));

二、实际应用案例

案例 1:React 组件测试

需求

测试一个简单的计数器组件,能够增加和减少计数。

组件代码

// Counter.js
import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
      <button onClick={() => setCount(count - 1)}>Decrease</button>
    </div>
  );
};

export default Counter;

测试代码

使用 renderfireEvent 方法来测试组件的功能。

// Counter.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Counter from './Counter';

test('initial count is 0', () => {
  const { getByText } = render(<Counter />);
  expect(getByText('0')).toBeInTheDocument();
});

test('increases count by 1 when Increase button is clicked', () => {
  const { getByText } = render(<Counter />);
  fireEvent.click(getByText('Increase'));
  expect(getByText('1')).toBeInTheDocument();
});

test('decreases count by 1 when Decrease button is clicked', () => {
  const { getByText } = render(<Counter />);
  fireEvent.click(getByText('Increase'));
  fireEvent.click(getByText('Decrease'));
  expect(getByText('0')).toBeInTheDocument();
});

案例 2:API 测试

需求

测试一个异步函数,从 API 获取数据。

API 函数代码

// api.js
export const fetchData = async () => {
  const response = await fetch('https://api.example.com/data');
  return response.json();
};

测试代码

使用 Mock 函数来模拟 API 响应。

// api.test.js
import { fetchData } from './api';

global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve({ data: 'mock data' }),
  })
);

test('fetches successfully data from an API', async () => {
  const data = await fetchData();
  expect(data).toEqual({ data: 'mock data' });
});

test('fetch fails with an error', async () => {
  fetch.mockImplementationOnce(() => Promise.reject('API is down'));
  await expect(fetchData()).rejects.toEqual('API is down');
});

案例 3:快照测试

组件代码

// Button.js
import React from 'react';

const Button = ({ label }) => (
  <button>{label}</button>
);

export default Button;

测试代码

使用快照测试验证组件的输出。

// Button.test.js
import React from 'react';
import renderer from 'react-test-renderer';
import Button from './Button';

test('Button renders correctly', () => {
  const tree = renderer.create(<Button label="Click me" />).toJSON();
  expect(tree).toMatchSnapshot();
});

三、常用 API 和方法

1. 测试用例 API

  • test(name, fn) :定义一个测试用例。
  • it(name, fn) :与 test 等效,描述行为。

2. 匹配器

  • expect(value) :用于断言某个值。

  • 常用匹配器:

    • toBe(value) :严格相等。
    • toEqual(value) :深度相等。
    • toBeTruthy() :值为真。
    • toContain(item) :检查数组或字符串中是否包含某个项。
    • toMatchSnapshot() :进行快照测试。

3. 异步匹配

  • resolves:测试 Promise 的成功结果。
  • rejects:测试 Promise 的失败结果。

4. Mock 函数 API

  • jest.fn() :创建一个新的 Mock 函数。
  • mockReturnValue(value) :设置 Mock 函数的返回值。
  • mockImplementation(fn) :设置 Mock 函数的实现。
  • mock.calls:获取 Mock 函数的调用记录。

5. 组装和清理 API

  • beforeEach(fn) :在每个测试用例之前运行的函数。
  • afterEach(fn) :在每个测试用例之后运行的函数。
  • beforeAll(fn) :在所有测试用例之前运行的函数。
  • afterAll(fn) :在所有测试用例之后运行的函数。

6. 运行测试的命令

  • jest --watch:以观察模式运行测试。
  • jest --coverage:生成代码覆盖率报告。