如何使用Node.js内置测试Runner模拟默认导出函数?
如何用Node.js原生测试Runner模拟默认导出的函数
核心方案:使用mock.module()拦截模块导出
Node.js测试runner的mock.fn()无法直接替换默认导出的函数(它仅包装函数,不修改模块引用),而mock.method()只适用于对象方法。正确的做法是用mock.module()拦截整个模块,替换默认导出为mock函数。
1. 自定义模块的默认导出模拟
针对你的defaultExport.js,测试代码示例:
import { test, mock } from 'node:test'; import assert from 'node:assert'; test('模拟默认导出的函数', async () => { // 拦截目标模块,替换默认导出为mock函数 const mockedModule = await mock.module('./defaultExport.js', (exports) => { exports.default = mock.fn(() => true); }); // 验证mock效果 assert.equal(mockedModule.default(), true); // 验证mock被调用过 assert.ok(mockedModule.default.mock.calls.length === 1); });
如果有依赖该默认导出的其他模块(比如你的wrapper函数),必须在mock之后加载依赖模块,确保它使用的是mock版本:
test('模拟被其他模块依赖的默认导出', async () => { // 先mock基础模块 await mock.module('./defaultExport.js', (exports) => { exports.default = mock.fn(() => true); }); // 在mock后加载依赖模块 const { wrapper } = await import('./wrapper.js'); assert.equal(wrapper(), true); });
2. 第三方模块的默认导出模拟(如knex)
对于无法修改的第三方模块,同样用mock.module()拦截替换:
import { test, mock } from 'node:test'; import assert from 'node:assert'; test('模拟第三方模块默认导出(如knex)', async () => { // 定义mock函数,模拟knex的行为 const mockedKnex = mock.fn(() => ({ select: mock.fn(() => Promise.resolve([])) })); // 拦截knex模块,替换默认导出 await mock.module('knex', (exports) => { exports.default = mockedKnex; }); // 加载使用knex的业务模块 const { queryData } = await import('./your-knex-usage.js'); const result = await queryData(); // 验证mock被调用 assert.ok(mockedKnex.mock.calls.length === 1); assert.deepEqual(result, []); });
为什么原方法失效?
mock.fn()仅包装传入的函数,不会修改模块导出的引用关系,调用原导入的函数时还是会执行原始逻辑。mock.method()只能作用于对象的属性方法,默认导出的独立函数不属于任何对象,因此无法使用。
备选方案:调整导出结构(自定义模块适用)
如果不想用mock.module(),可以在自定义模块中同时保留默认导出和命名导出,兼顾简洁性和可测试性:
// defaultExport.js export default function myFunction() { return false; } export { myFunction };
测试时通过命名导出进行mock:
import { test, mock } from 'node:test'; import assert from 'node:assert'; import { myFunction } from './defaultExport.js'; test('通过命名导出模拟', () => { const mockedFn = mock.fn(() => true); // 替换当前模块中的命名导出引用 globalThis.myFunction = mockedFn; assert.equal(mockedFn(), true); });
注:这种方法仅在当前测试模块有效,依赖默认导出的其他模块不会同步生效,仅适合简单场景。
内容的提问来源于stack exchange,提问作者bookcasey
相关产品推荐
相关产品推荐

