
本文深入探讨了在 jest 测试框架中,如何有效管理手动模拟(manual mock)与实际模块的切换使用。通过详细介绍 `jest.dontmock()` 和 `jest.resetmodules()` 这两个关键 api,文章提供了在特定测试场景下绕过全局手动模拟、调用真实模块实现的实用方法,并辅以代码示例和注意事项,旨在帮助开发者实现更灵活、精准的测试控制。
在 Jest 中,当我们为某个模块(例如 axios)创建了手动模拟文件(如 __mocks__/axios.js),Jest 会在测试运行时自动使用这个模拟版本,而不是实际的 axios 包。这种机制在大多数情况下非常方便,它允许我们隔离外部依赖,专注于测试当前模块的逻辑。
然而,有时我们可能需要在同一个测试套件中,针对某些特定测试用例调用模块的真实实现。例如,在集成测试或端到端测试中,我们可能希望 axios 真正地发出 HTTP 请求。直接在测试内部使用 jest.mock("axios", () => jest.requireActual("axios")) 尝试覆盖模拟可能不会立即生效,这是因为 Jest 有一个模块注册表缓存机制。一旦模块被加载并模拟,其模拟版本就会被缓存起来,后续的 require 或 import 语句会直接从缓存中获取,而不会重新解析或应用新的模拟指令。
为了在特定测试中绕过手动模拟并加载真实模块,我们需要结合使用 jest.dontMock(moduleName) 和 jest.resetModules()。
jest.dontMock(moduleName): 这个 API 告诉 Jest 不要模拟指定的模块。它会阻止 Jest 使用 __mocks__ 目录下的手动模拟文件,而是加载模块的实际实现。
jest.resetModules(): 这个 API 用于重置 Jest 的模块注册表缓存。它会清除所有已加载模块的缓存,确保下一次 require 或 import 操作会重新加载模块,从而使得 jest.dontMock() 的效果得以应用。
重要提示: jest.resetModules() 必须在 jest.dontMock() 之前调用,或者至少在模块被 import 之前调用,以确保模块注册表被清除,并且 jest.dontMock() 能够影响到后续的模块加载行为。通常,我们会在 beforeEach 钩子中调用 jest.resetModules(),以确保每个测试用例都在一个“干净”的模块环境中运行。
下面通过一个具体的例子来演示如何实现手动模拟与实际模块的切换。
假设我们有一个 axios 的手动模拟,它总是返回一个固定的假数据:
__mocks__/axios.js:
let mockAxios = jest.genMockFromModule('axios');
// 为 get 方法提供一个默认的模拟实现
mockAxios.get = jest.fn().mockResolvedValue({ data: 'fake' });
export default mockAxios;接着,我们有一个业务逻辑模块 main.ts,它使用了 axios 发送 HTTP 请求:
main.ts:
import axios from 'axios';
export function main() {
return axios.get('https://jsonplaceholder.typicode.com/todos/1');
}现在,我们编写测试文件 main.test.ts,其中包含一个使用模拟 axios 的测试和一个使用真实 axios 的测试:
main.test.ts:
describe('Axios 模块模拟与真实实现切换', () => {
// 在每个测试用例运行前重置模块注册表
// 这一步至关重要,它清除了之前测试可能留下的模块缓存,
// 确保每个测试用例都能根据其自身的模拟配置加载模块。
beforeEach(() => {
jest.resetModules();
});
test('应该使用模拟的 axios', async () => {
// 默认情况下,如果存在手动模拟,Jest 会使用它。
// 在此测试中,我们不需要额外的 dontMock,因为我们希望使用模拟。
const { main } = await import('./main'); // 导入 main 模块,此时 axios 会被模拟
const actual = await main();
console.log('使用模拟 axios 的结果:', actual?.data);
expect(actual?.data).toEqual('fake'); // 预期返回模拟数据
});
test('应该使用真实的 axios', async () => {
// 在这个测试用例中,我们明确告诉 Jest 不要模拟 axios
jest.dontMock('axios');
// 再次导入 main 模块。由于 resetModules 和 dontMock 的作用,
// 此时 main 内部的 axios 将是真实实现。
const { main } = await import('./main');
const actual = await main();
console.log('使用真实 axios 的结果:', actual?.data);
// 预期返回真实 API 的数据结构
expect(actual?.data).toHaveProperty('userId');
expect(actual?.data).toHaveProperty('id');
expect(actual?.data).toHaveProperty('title');
expect(actual?.data).toHaveProperty('completed');
});
});运行上述测试,你将观察到以下输出:
console.log
使用模拟 axios 的结果: fake
console.log
使用真实 axios 的结果: { userId: 1, id: 1, title: 'delectus aut autem', completed: false }这清楚地表明,第一个测试用例成功使用了手动模拟的 axios,而第二个测试用例通过 jest.dontMock('axios') 和 jest.resetModules() 成功加载并使用了真实的 axios 模块,并从外部 API 获取了数据。
通过熟练运用 jest.dontMock() 和 jest.resetModules(),开发者可以在 Jest 测试中获得对模块模拟行为的精细控制。这使得在同一个测试套件中,既能利用手动模拟进行隔离测试,又能按需调用真实模块进行更接近生产环境的集成测试,从而极大地提升了测试的灵活性和覆盖范围。理解并正确使用这些 Jest API 是编写健壮、可维护测试的关键。
以上就是在 Jest 中灵活切换手动 Mock 与实际模块实现的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号