在单元测试中模拟 es6 模块

mocking es6 modules in unit test

假设我们有一个文件(source.js)要测试:

// source.js
import x from './x';

export default () => x();

并且单元测试代码非常简单:

// test.js
import test from 'ava';
import source from './source'

test("OK", t => {
  source();
  t.pass();
});

但这是棘手的事情。文件“./x”在测试环境中不存在。由于是单元测试,我们不需要“./x”。我们可以模拟函数 "x()" 无论如何(使用 sinon 或其他东西)。但是单元测试工具ava一直显示"Error: Cannot find module './x'";

有什么方法可以运行没有文件“./x”的单元测试吗?

为此,您需要覆盖导入过程本身。有一些库可以做类似的事情——例如,用 proxyquire 覆盖 require 函数——但这些库会强制您调用它们的自定义函数来导入被测模块。换句话说,你需要放弃使用 ES6 模块语法才能使用它们。

您还应该注意,ES 模块仅在 Node.js 中实验性地(并且相对最近)得到支持。如果您使用 Babel 进行转译,您实际上 使用的不是 ES 模块。当然,您正在使用语法,但 Babel 将它们转换为 CommonJS "equivalents",完成 require 调用和 module.exports 赋值。

因此,如果您使用 Babel,您可能 proxyquire 在测试文件中导入被测模块,即使您在这些模块中使用 ES 模块语法。但是,如果您离开 Babel,这将会中断。 :\

我个人的建议是避免直接导出您可能需要存根的任何内容。所以像 x 这样的函数应该像 import foo from './foo' 一样被导入到一个静态模块中,并且像 foo.x() 一样被调用。然后,您可以使用 sinon.stub(foo, 'x') 轻松地存根它。当然,'./foo' 文件仍然必须存在,但它真正归结为您希望 TDD 实践的核心程度与您愿意为 [=31] 引入多少复杂性=] 过程。我更喜欢放宽前者以避免后者,但最终取决于你。

如果你在 JavaScript 中使用 jest,这很简单:

使用jest.mock('MODULE_NAME')

如果是node模块或者你自己的模块,没问题jest会自动mock那个模块。

如果您使用的是 Jest,可以通过使用 jest 的内置 mock 方法模拟导入来轻松实现。 请参阅下面的 link 以了解有关 ES6 导入模拟的更多信息

https://jestjs.io/docs/en/es6-class-mocks