乐闻世界logo
搜索文章和话题

Jest

Jest 是一个流行的 JavaScript 测试框架,用于编写和运行测试。它由 Facebook 开发,并被应用于测试 React 组件以及其他类型的 JavaScript 代码。Jest 被设计为零配置,易于上手,同时提供了丰富的特性,如快照测试、内置的覆盖率报告和模拟系统。
Jest
查看更多相关内容
Jest 中有哪些测试匹配器(Matchers)?如何使用自定义匹配器?Jest 提供了多种测试匹配器(Matchers)来验证不同的条件: **相等性匹配器:** - `toBe(value)`:严格相等(`===`) - `toEqual(value)`:深度相等 - `toStrictEqual(value)`:严格深度相等(包括 undefined 属性) - `toMatchObject(object)`:部分匹配对象 **真值匹配器:** - `toBeNull()`:只匹配 null - `toBeUndefined()`:只匹配 undefined - `toBeDefined()`:非 undefined - `toBeTruthy()`:真值 - `toBeFalsy()`:假值 **数字匹配器:** - `toBeGreaterThan(number)`:大于 - `toBeGreaterThanOrEqual(number)`:大于等于 - `toBeLessThan(number)`:小于 - `toBeLessThanOrEqual(number)`:小于等于 - `toBeCloseTo(number, precision)`:浮点数近似相等 **字符串匹配器:** - `toMatch(regexp | string)`:匹配正则或字符串 - `toContain(item)`:包含元素或子字符串 **数组匹配器:** - `toContain(item)`:包含元素 - `toContainEqual(item)`:包含相等元素 - `toHaveLength(number)`:数组长度 - `toBeArray()`:是数组 **对象匹配器:** - `toHaveProperty(keyPath, value)`:有特定属性 - `toMatchObject(object)`:部分匹配对象 **函数匹配器:** - `toHaveBeenCalled()`:被调用 - `toHaveBeenCalledWith(...args)`:用特定参数调用 - `toHaveBeenCalledTimes(n)`:调用次数 - `toHaveReturned()`:返回值 - `toHaveReturnedWith(value)`:返回特定值 **异常匹配器:** - `toThrow(error?)`:抛出错误 - `toThrowErrorMatchingSnapshot()`:错误快照 **自定义匹配器:** ```javascript expect.extend({ toBeWithinRange(received, floor, ceiling) { const pass = received >= floor && received <= ceiling; return { pass, message: () => pass ? `expected ${received} not to be within range ${floor}-${ceiling}` : `expected ${received} to be within range ${floor}-${ceiling}` }; } }); test('number within range', () => { expect(100).toBeWithinRange(90, 110); }); ``` **否定匹配器:** 所有匹配器都可以使用 `.not` 进行否定: ```javascript expect(value).not.toBe(42); expect(array).not.toContain('item'); ```
服务端 · 2月21日 15:57
如何在 Jest 中测试 React Hooks?如何使用 renderHook 和 act?Jest 提供了多种测试 React Hooks 的方法,主要使用 `@testing-library/react-hooks`: **1. 测试 useState:** ```javascript import { renderHook, act } from '@testing-library/react-hooks'; test('useState hook', () => { const { result } = renderHook(() => useCounter(0)); expect(result.current.count).toBe(0); act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); }); ``` **2. 测试 useEffect:** ```javascript test('useEffect hook', () => { const { result } = renderHook(() => useFetch('/api/data')); expect(result.current.loading).toBe(true); await act(async () => { await waitFor(() => !result.current.loading); }); expect(result.current.data).toBeDefined(); }); ``` **3. 测试 useContext:** ```javascript test('useContext hook', () => { const wrapper = ({ children }) => ( <ThemeContext.Provider value="dark"> {children} </ThemeContext.Provider> ); const { result } = renderHook(() => useTheme(), { wrapper }); expect(result.current.theme).toBe('dark'); }); ``` **4. 测试自定义 Hook:** ```javascript function useCustomHook(initialValue) { const [value, setValue] = useState(initialValue); const double = useMemo(() => value * 2, [value]); return { value, setValue, double }; } test('custom hook', () => { const { result } = renderHook(() => useCustomHook(5)); expect(result.current.value).toBe(5); expect(result.current.double).toBe(10); act(() => { result.current.setValue(10); }); expect(result.current.double).toBe(20); }); ``` **5. 测试异步 Hook:** ```javascript test('async hook', async () => { const { result, waitForNextUpdate } = renderHook(() => useAsyncData()); expect(result.current.loading).toBe(true); await waitForNextUpdate(); expect(result.current.loading).toBe(false); expect(result.current.data).toBeDefined(); }); ``` **6. 测试错误处理:** ```javascript test('hook error handling', async () => { const { result, waitForNextUpdate } = renderHook(() => useFetch('/invalid')); await waitForNextUpdate(); expect(result.current.error).toBeInstanceOf(Error); }); ``` **最佳实践:** - 使用 `renderHook` 测试 Hook - 使用 `act` 包装状态更新 - 测试 Hook 的初始状态和更新后的状态 - 测试异步 Hook 时使用 `waitFor` 或 `waitForNextUpdate` - 测试错误边界情况 - 保持测试简单,专注于 Hook 的行为
服务端 · 2月21日 15:57
如何在 Jest 中测试定时器(setTimeout、setInterval)?如何使用假定时器?Jest 提供了多种处理定时器的方法,用于测试涉及 setTimeout、setInterval 等定时器的代码: **1. 使用假定时器:** ```javascript jest.useFakeTimers(); test('timer callback', () => { const callback = jest.fn(); setTimeout(callback, 1000); jest.runAllTimers(); expect(callback).toHaveBeenCalledTimes(1); }); ``` **2. 运行所有定时器:** ```javascript jest.runAllTimers(); // 运行所有待处理的定时器 ``` **3. 运行到特定时间:** ```javascript jest.advanceTimersByTime(1000); // 前进 1000ms jest.runOnlyPendingTimers(); // 只运行当前待处理的定时器 ``` **4. 测试 setInterval:** ```javascript test('interval timer', () => { const callback = jest.fn(); setInterval(callback, 1000); jest.advanceTimersByTime(3000); expect(callback).toHaveBeenCalledTimes(3); }); ``` **5. 清理定时器:** ```javascript afterEach(() => { jest.useRealTimers(); // 恢复真实定时器 jest.clearAllTimers(); // 清除所有定时器 }); ``` **6. 测试定时器清除:** ```javascript test('clear timeout', () => { const callback = jest.fn(); const timeoutId = setTimeout(callback, 1000); clearTimeout(timeoutId); jest.runAllTimers(); expect(callback).not.toHaveBeenCalled(); }); ``` **最佳实践:** - 在测试套件开始时使用 `jest.useFakeTimers()` - 测试完成后恢复真实定时器 - 使用 `advanceTimersByTime` 测试时间依赖逻辑 - 确保清理所有定时器以避免内存泄漏
服务端 · 2月21日 15:57
Jest 中的生命周期钩子有哪些?beforeAll、afterAll、beforeEach 和 afterEach 如何使用?Jest 提供了多个生命周期钩子函数来管理测试的设置和清理: **beforeAll:** - 在当前测试套件的所有测试运行之前执行一次 - 适用于一次性设置,如数据库连接 - 示例:`beforeAll(() => { connectDatabase(); });` **afterAll:** - 在当前测试套件的所有测试运行之后执行一次 - 适用于清理资源,如关闭数据库连接 - 示例:`afterAll(() => { disconnectDatabase(); });` **beforeEach:** - 在当前测试套件的每个测试运行之前执行 - 适用于每个测试的独立设置,如重置状态 - 示例:`beforeEach(() => { resetState(); });` **afterEach:** - 在当前测试套件的每个测试运行之后执行 - 适用于每个测试的清理,如清除 Mock - 示例:`afterEach(() => { jest.clearAllMocks(); });` **作用域:** - 钩子函数在定义的 `describe` 块内生效 - 嵌套的 `describe` 会继承父级钩子 - 可以在多个层级定义钩子 **示例:** ```javascript describe('User API', () => { beforeAll(() => setupServer()); afterAll(() => teardownServer()); beforeEach(() => { jest.clearAllMocks(); }); test('should create user', () => { /* ... */ }); test('should get user', () => { /* ... */ }); }); ``` **最佳实践:** - 使用 `beforeEach` 和 `afterEach` 确保测试隔离 - 使用 `beforeAll` 和 `afterAll` 优化性能 - 在 `afterEach` 中清理 Mock 和定时器 - 保持钩子函数简单,避免复杂逻辑
服务端 · 2月21日 15:57
如何在 Jest 中进行参数化测试?如何使用 test.each 和 describe.each?Jest 提供了参数化测试的方法,可以使用 `test.each` 或 `describe.each` 来运行多组测试数据: **1. 使用 test.each 进行参数化测试:** ```javascript test.each([ [1, 1, 2], [1, 2, 3], [2, 1, 3], ])('adds %i + %i = %i', (a, b, expected) => { expect(add(a, b)).toBe(expected); }); ``` **2. 使用对象数组:** ```javascript test.each([ { a: 1, b: 1, expected: 2 }, { a: 1, b: 2, expected: 3 }, { a: 2, b: 1, expected: 3 }, ])('$a + $b = $expected', ({ a, b, expected }) => { expect(add(a, b)).toBe(expected); }); ``` **3. 使用 describe.each 进行分组测试:** ```javascript describe.each([ ['node', 'node'], ['jsdom', 'browser'], ])('test environment: %s', (env, type) => { test(`runs in ${type} environment`, () => { expect(process.env.NODE_ENV).toBeDefined(); }); }); ``` **4. 测试表格数据:** ```javascript test.each` a | b | expected ${1} | ${1} | ${2} ${1} | ${2} | ${3} ${2} | ${1} | ${3} `('returns $expected when $a is added to $b', ({ a, b, expected }) => { expect(add(a, b)).toBe(expected); }); ``` **5. 测试边界情况:** ```javascript test.each([ [0, 0, 0], [Number.MAX_SAFE_INTEGER, 1, Number.MAX_SAFE_INTEGER + 1], [Number.MIN_SAFE_INTEGER, -1, Number.MIN_SAFE_INTEGER - 1], ])('handles edge cases: %i + %i = %i', (a, b, expected) => { expect(add(a, b)).toBe(expected); }); ``` **6. 测试错误情况:** ```javascript test.each([ [undefined, 'input is required'], [null, 'input is required'], ['', 'input cannot be empty'], ])('throws error for invalid input: %p', (input, expectedError) => { expect(() => validate(input)).toThrow(expectedError); }); ``` **最佳实践:** - 使用参数化测试减少重复代码 - 清晰描述测试数据和预期结果 - 测试正常情况和边界情况 - 使用表格格式提高可读性 - 保持测试数据简洁明了
服务端 · 2月21日 15:57
Jest 中的 Mock 功能如何使用?如何创建 Mock 函数和模拟模块?Jest 的 Mock 功能是测试中隔离依赖和验证行为的重要工具: **1. 创建 Mock 函数:** ```javascript const mockFn = jest.fn(); mockFn('arg1', 'arg2'); expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2'); expect(mockFn).toHaveBeenCalledTimes(1); ``` **2. Mock 返回值:** ```javascript const mockFn = jest.fn(); mockFn.mockReturnValue(42); mockFn.mockReturnValueOnce(10).mockReturnValueOnce(20); // 或使用 mockResolvedValue 处理 Promise mockFn.mockResolvedValue('data'); ``` **3. Mock 实现:** ```javascript const mockFn = jest.fn((a, b) => a + b); // 或使用 mockImplementation mockFn.mockImplementation((a, b) => a * b); ``` **4. Mock 模块:** ```javascript jest.mock('./api', () => ({ fetchData: jest.fn(() => Promise.resolve('data')) })); import { fetchData } from './api'; ``` **5. Spy 函数:** ```javascript const spy = jest.spyOn(object, 'method'); spy.mockRestore(); // 恢复原函数 ``` **常用断言:** - `toHaveBeenCalled()`:被调用 - `toHaveBeenCalledWith(...args)`:用特定参数调用 - `toHaveBeenCalledTimes(n)`:调用次数 - `toHaveReturnedWith(value)`:返回特定值 - `toHaveLastReturnedWith(value)`:最后一次返回值 **最佳实践:** - Mock 外部依赖,不 Mock 被测代码 - 测试完成后清理 Mock - 使用 `jest.clearAllMocks()` 清除调用记录 - 使用 `jest.restoreAllMocks()` 恢复原始实现
服务端 · 2月21日 15:54
如何在 Jest 中生成和配置代码覆盖率报告?覆盖率指标有哪些?Jest 提供了强大的代码覆盖率报告功能,帮助开发者了解测试覆盖情况: **生成覆盖率报告:** ```bash # 运行测试并生成覆盖率报告 jest --coverage # 或在 package.json 中配置 { "scripts": { "test:coverage": "jest --coverage" } } ``` **覆盖率指标:** - **Statements(语句覆盖率)**:被执行的代码语句百分比 - **Branches(分支覆盖率)**:被执行的条件分支百分比 - **Functions(函数覆盖率)**:被调用的函数百分比 - **Lines(行覆盖率)**:被执行的代码行百分比 **配置覆盖率:** ```javascript // jest.config.js module.exports = { collectCoverage: true, collectCoverageFrom: [ 'src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts', '!src/**/*.stories.{js,jsx,ts,tsx}', '!src/index.{js,jsx,ts,tsx}' ], coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } }, coverageReporters: ['text', 'lcov', 'html'] }; ``` **覆盖率报告格式:** - `text`:命令行文本输出 - `lcov`:生成 lcov.info 文件,用于 CI/CD 工具 - `html`:生成 HTML 报告,可视化查看 - `json`:JSON 格式报告 **最佳实践:** - 设置合理的覆盖率阈值(通常 80%) - 排除不需要测试的文件(配置文件、类型定义等) - 在 CI/CD 中集成覆盖率检查 - 定期审查未覆盖的代码 - 关注核心业务逻辑的覆盖率
服务端 · 2月21日 15:54