跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

Jest 中正确模拟只读导出函数(如 GetData)的完整教程

本文详解如何在 Jest 中成功模拟来自依赖模块的只读异步函数(如 export async function GetData()),通过 jest.spyOn() 配合模块导入实现可靠、符合 ESM 规范的单元测试。 本文详解如何在 jest 中成功模拟来自依赖模块的只读异步函数(如 `export async function getdata()`),通过 `jest.spyon()` 配合模块导入实现可靠、符合 esm 规范的单元测试。 在使用 Jest 测试基于 ES 模块(ESM)的代码时,常遇到一个典型痛点:无法直接重写或赋值被 export 的函数(如 export async function GetData()),因其在模块作用域中为只读属性。此时 jest.mock() 的工厂函数方式可能因模块解析时机问题失效,而错误地对调用方类(如 MainClass)进行 spyOn 则根本找不到目标方法——因为 GetData 并非 MainClass 的静态成员,而是独立导出的模块级函数。 ✅ 正确做法是:* 显式导入目标模块为命名空间对象(`import as xxx from './xxx'),再对其属性调用jest.spyOn()`**。该方式直接操作模块的导出对象,在 Jest 的模块模拟机制下完全合法且稳定。 以下为可直接运行的完整测试示例:
// __tests__/mainClass.test.js import { MainClass } from '../mainClass'; import * as childClassModule from '../childClass'; // ✅ 关键:导入整个模块 describe('MainClass.execute', () => { beforeEach(() => { // ✅ 正确目标:spyOn childClassModule 对象上的 GetData 属性 // ✅ 因 GetData 是 async 函数,必须使用 mockResolvedValue(而非 mockReturnValue) jest.spyOn(childClassModule, 'GetData').mockResolvedValue({ data: 'mocked response' }); }); afterEach(() => { // ? 清理 mock,避免测试间污染(尤其在 watch 模式下) jest.restoreAllMocks(); }); test('should return resolved value from mocked GetData', async () => { const result = await MainClass.execute(); expect(result).toEqual({ data: 'mocked response' }); }); });
⚠️ 注意事项: 不要使用 jest.mock('./childClass', ...) 工厂函数 :它会创建全新模块实例,可能导致 MainClass 加载的是原始模块而非 mock 版本(尤其在未启用 resetModules 或存在缓存时); beforeAll 不推荐用于模块级 spy :模块导入发生在 describe 外部,若在 beforeAll 中 spy,可能因执行时机早于模块实际加载而失败;beforeEach 更安全可控; 必须匹配函数签名 :GetData 返回 Promise,因此必须用 mockResolvedValue() 或 mockImplementation(() => Promise.resolve(...)); 务必调用 jest.restoreAllMocks() :否则 mock 会持续影响后续测试用例,引发隐蔽的 flaky test; 若 childClass.js 使用默认导出(如 export default { GetData }),则需改为 import childClassModule from '../childClass' 并 spyOn(childClassModule, 'GetData')。 总结:模拟只读导出函数的核心在于「精准定位导出源」——不是调用它的类,而是它所属的模块对象本身。通过 import * as 显式获取模块命名空间,并结合 jest.spyOn(...).mockResolvedValue(),即可优雅、可靠地完成异步函数的隔离测试,无需修改源码或绕道 require/jest.requireActual 等非常规手段。

相关文章