Jest - 性能和高级主题
在本章中,我们将介绍高级 Jest 功能,以帮助您编写更快、更高效的测试。我们将介绍的主题包括:
Jest - 性能优化
优化 Jest 的性能对于大型项目或处理许多测试用例时非常重要。以下是一些可用于加速 Jest 测试的策略:
并行运行测试
默认情况下,Jest 并行运行测试,从而加快测试执行速度。它根据 CPU 核心数将测试划分为多个工作线程(进程),有助于减少测试运行时间。
您可以使用 maxWorkers 选项在 Jest 配置文件(jest.config.js)中调整工作线程数量。
module.exports = { maxWorkers: "50%", // 使用 50% 的可用 CPU 核心 };
避免使用大型测试套件
对于大型测试套件,请通过将测试组织到单独的文件中或使用 testPathPattern 选项运行特定测试,将其拆分为较小的块。
jest --testPathPattern="specificTestFile"
避免缓慢的安装/拆卸
要优化缓慢的安装或拆卸(如数据库连接),请使用beforeAll和afterAll以避免在beforeEach和afterEach中重复操作。
使用jest.runAllTimers()和jest.clearAllMocks()
要提高测试性能,请清除未使用的模拟和计时器以防止运行不必要的代码。例如:
beforeEach(() => { jest.clearAllMocks(); // 清除不再需要的任何模拟 jest.runAllTimers(); // 立即运行所有待处理的计时器 });
对 CI 环境使用 --silent 标志
在持续集成 (CI) 环境中,使用 --silent 标志可防止控制台中出现不必要的输出,这有助于减少测试执行期间的负载。
jest --silent
优化测试设置和拆卸
避免在 beforeAll 或 afterAll 中进行繁重操作。尽量减少设置和拆卸,以加快执行速度。如果您的测试与数据库交互,请考虑模拟数据库以提高性能。
Jest - 自定义匹配器
Jest 中的自定义匹配器允许您创建自己的期望,使您的测试更清晰,更适合您的需求。它们对于测试特定行为或自定义数据结构特别有用。
创建自定义匹配器
要创建自定义匹配器,请使用 expect.extend()。以下是定义检查数字是否可以被另一个数字整除的匹配器的方法:
expect.extend({ toBeDivisibleBy(received, argument) { const pass = received % argument === 0; if (pass) { return { message: () => `expected ${received} not to be divisible by ${argument}`, pass: true, }; } else { return { message: () => `expected ${received} to be divisible by ${argument}`, pass: false, }; } }, });
使用自定义匹配器
定义自定义匹配器后,您可以在测试中使用它,如下所示:
test('is divisible by 3', () => { expect(6).toBeDivisibleBy(3); // 传递 expect(7).toBeDivisibleBy(3); // 失败 });
异步自定义匹配器
您还可以为涉及 Promise 或异步操作的测试创建异步自定义匹配器。以下是异步匹配器的示例,用于检查从 URL 获取的数据是否包含特定值:
expect.extend({ async toFetchData(received) { const response = await fetch(received); const data = await response.json(); return { message: () => `expected ${received} to fetch data`, pass: data.someProperty === 'expectedValue', }; }, });
Jest - 计时器模拟
计时器(例如 setTimeout 和 setInterval)在 JavaScript 中很常见。Jest 提供了在测试中模拟和控制它们的工具,允许您测试与时间相关的函数,而无需等待实际时间流逝。
使用 Jest 模拟计时器
要在 Jest 中模拟计时器,请在测试开始时使用 jest.useFakeTimers()。这会将默认计时器替换为您可以控制的模拟计时器。
beforeEach(() => { jest.useFakeTimers(); // 启用假计时器 }); afterEach(() => { jest.useRealTimers(); // 每次测试后恢复真实计时器 });
推进计时器
一旦计时器被模拟,您可以使用 jest.advanceTimersByTime() 或 jest.runAllTimers()
手动推进它们- jest.advanceTimersByTime(ms): 将计时器推进指定的毫秒数。
test('calls the callback after timeout', () => { const callback = jest.fn(); setTimeout(callback, 1000); jest.advanceTimersByTime(1000); // Fast-forward 1000ms expect(callback).toHaveBeenCalled(); });
test('executes all timers', () => { const callback = jest.fn(); setTimeout(callback, 500); setTimeout(callback, 1000); jest.runAllTimers(); // Executes both timeouts expect(callback).toHaveBeenCalledTimes(2); });
清除模拟计时器
为防止测试相互影响,请使用 jest.clearAllTimers() 清除模拟计时器,或在测试之间使用 jest.useRealTimers() 恢复原始计时器。
afterEach(() => { jest.clearAllTimers(); });
Jest - DOM 测试
当与 React Testing Library 或 Enzyme 等库一起使用时,Jest 非常适合DOM 测试。这些库使用 jsdom 模拟 Node.js 中的 DOM 环境。
使用 Jest 设置 DOM 测试
Jest 默认使用 jsdom,因此无需额外设置。要与 DOM 交互,请安装 React Testing Library 或 Enzyme。
要安装 React Testing Library:
npm install @testing-library/react @testing-library/jest-dom
编写 DOM 测试
设置完成后,您可以为组件编写测试。以下是如何在 React 组件中测试按钮点击的示例。
import { render, screen, fireEvent } from '@testing-library/react'; import MyComponent from './MyComponent'; test('button click changes text', () => { render(<MyComponent />); const button = screen.getByRole('button'); fireEvent.click(button); const text = screen.getByText(/button clicked/i); expect(text).toBeInTheDocument(); });
使用 Jest DOM 进行断言
jest-dom 为 Jest 添加了额外的匹配器,用于测试 DOM 元素。一些有用的匹配器是:
- .toBeInTheDocument(): 检查元素是否在 DOM 中。
- .toHaveTextContent(): 验证元素是否包含给定的文本。
- .toBeVisible(): 确保元素在屏幕上可见。
示例
import '@testing-library/jest-dom'; // Provides extra matchers test('button displays correct text', () => { render(<MyComponent />); const button = screen.getByRole('button'); expect(button).toHaveTextContent('Click me'); expect(button).toBeVisible(); });
测试后清理
Jest 会在每次测试后自动清理 DOM。但是,如果需要,您也可以使用 React Testing Library 中的 cleanup() 函数手动清除 DOM。
示例
import { cleanup } from '@testing-library/react'; afterEach(() => { jest.clearAllMocks(); cleanup(); });
这将在每次测试后清除 DOM,以防止测试之间的干扰。