Jest - 最佳实践
在本章中,我们将介绍使用 Jest 编写更快、更可靠的测试的最佳实践、要避免的常见错误以及如何组织测试文件。通过遵循这些做法,您将保持测试的可维护性和高效性。
Jest - 测试最佳实践
以下是使用 Jest 编写清晰、高效和可靠的测试的一些最佳实践:
- 保持测试小而有重点:每个测试都应关注单一行为。这使得理解和快速识别错误变得更加容易。
- 使用描述性测试名称:为您的测试提供清晰、描述性的名称,以解释它们的作用。这有助于其他人了解正在测试的内容以及预期结果。
- 测试行为,而不是实现:关注代码的作用,而不是它如何执行。这样,即使代码发生变化,您的测试仍然有用。
- 正确使用设置和拆卸:使用beforeEach和afterEach在测试前后进行设置和清理。这有助于保持测试隔离并确保每个测试都处于干净的状态。
- 保持测试独立:每个测试都应该独立。一个测试不应依赖于另一个测试的结果。这使测试更可靠,更容易排除故障。
- 测试边缘情况:测试您的代码如何处理意外或极端的输入,例如空值、大数字或无效数据。这可确保您的代码在所有情况下都能正常工作。
- 使用 Mocks 和 Spies:使用 mocks 和 spies 隔离您正在测试的代码,尤其是在测试与外部系统(如 API 或数据库)交互的函数时。这可使测试快速而集中。
- 避免测试私有方法:不要直接测试私有方法。相反,专注于测试代码的公共行为。这可使测试更加灵活,并且更少地依赖于实现细节。
- 对异步代码使用 async/await:对于 async 代码,使用 async/await 确保您的测试在检查结果之前等待承诺解决。这使测试更易于阅读且不易出错。
- 并行运行测试:Jest 默认并行运行测试,从而加快测试速度。确保您的测试不依赖于共享状态,这样它们就可以独立且更快地运行。
Jest - 应避免的常见错误
Jest 是一个强大的测试工具,但有一些常见错误可能会使您的测试不可靠。以下是一些常见错误及其避免方法:
未清理模拟
使用模拟时,每次测试后清理它们非常重要,以避免内存泄漏或测试之间的干扰。
解决方案:在 afterEach 块内使用 jest.clearAllMocks() 或 jest.resetAllMocks()。
afterEach(() => { jest.clearAllMocks(); // 每次测试后重置模拟 });
在测试中过度使用 setTimeout 或 setInterval
使用 setTimeout 或 setInterval 会减慢测试速度并使其不可靠,尤其是在使用异步代码时。
解决方案:使用 jest.useFakeTimers() 模拟计时器并控制测试中的时间流。
jest.useFakeTimers(); // 模拟计时器以加快测试速度
未对异步测试使用 async/await
测试异步代码时始终使用 async/await。避免使用回调,因为它们可能会导致处理异步行为的问题。
解决方案:使用异步函数或从测试中返回承诺。
test('should fetch data correct', async () => { const data = await fetchData(); expect(data).toBeDefined(); // 确保已获取数据 });
未处理异步测试中的错误
测试异步代码时,始终处理可能的错误。如果不处理错误,则可能导致测试意外失败。
解决方案:使用try/catch或.catch()处理异步测试中的错误。
test('should throw an error if data is invalid', async () => { try { await fetchData(); } catch (e) { expect(e).toBeDefined(); // Check for errors } });
未使用正确的断言
断言对于检查预期结果是否与实际输出匹配非常重要。使用正确的断言方法来验证测试结果。
解决方案:正确使用 Jest 的断言 方法(如 toBe、toBeTruthy、toHaveLength)。
expect(result).toBe(true); // 检查是否严格相等 expect(result).toBeTruthy(); // 检查是否真实 expect(result).toHaveLength(3); // 检查数组长度
使测试过于复杂
测试应该简单易懂。避免在一个测试用例中测试太多内容或使用太多断言。
解决方案:将复杂的逻辑分解为更小、更易于管理的测试。
// Bad practice test('should process data and render components correctly', () => { const result = processData(); render(<Component data={result} />); expect(screen.getByText('Some Data')).toBeInTheDocument(); }); // 更好的做法:分成多个测试 test('should process data correctly', () => { const result = processData(); expect(result).toBeDefined(); // 单独测试数据处理 }); test('should render component with processed data', () => { render(<Component data={processedData} />); expect(screen.getByText('Some Data')).toBeInTheDocument(); // 单独测试渲染 });
忽略测试覆盖率
高测试覆盖率并不能保证无错误代码,但跟踪代码的哪些部分正在测试非常重要。
解决方案:使用 Jest 的 --coverage 选项检查代码的哪些部分被测试覆盖。
jest --coverage // 运行带有覆盖率报告的测试
确保测试所有重要路径(包括边缘情况)。
Jest - 代码组织
组织好 Jest 测试有助于使代码更易于维护和扩展。良好的组织结构可让您轻松查找、更新和运行测试。
按功能组织测试
将相关功能的测试分组到各自的文件夹中。这样,当您更改功能时,可以更轻松地查找和更新测试。

使用 __tests__ 文件夹存放测试文件
您可以为所有测试文件创建单独的 __tests__ 文件夹,而不是将测试放在代码旁边。这可让您的项目更整洁。

测试文件的命名约定
为测试文件使用清晰的名称。常见的惯例是使用 .test.js 或 .spec.js 作为测试文件(例如 Card.test.js 或 Header.spec.js)。
模拟和存根
如果您要模拟 API 或数据库等依赖项,请将它们放在 __mocks__ 文件夹中。这样,您可以在多个测试中重复使用模拟。

对于仅在单个测试中使用的模拟,您可以在该测试中直接模拟它们。
jest.mock('axios');
将测试放在靠近代码的位置
将测试文件放在靠近测试代码的位置。这样可以更轻松地同时更新代码和测试。如果愿意,您还可以使用 __tests__ 文件夹来存储测试。
使用 describe 对测试进行分组
使用 describe 块将相关测试分组在一起。这样可以清楚地了解正在测试的代码部分。
describe('User authentication', () => { test('should log in with valid credentials', () => { expect(login('user', 'password')).toBe(true); }); test('should reject invalid credentials', () => { expect(login('user', 'wrong-password')).toBe(false); }); });
确保一致的测试设置
使用 Jest 的设置函数(如 beforeAll、afterAll、beforeEach 和 afterEach)确保所有测试都具有相同的环境设置。
beforeEach(() => { jest.resetModules(); // Reset module cache before each test });