Jest:存根 ESM 模块命名导出的标准方法

ldi*_*ual 5 javascript stub spy jestjs es6-modules

这个问题并不是 Jest 特有的,因为它适用于所有具有存根功能的测试库。

\n

ESM 模块具有不可变的命名和默认导出,这意味着这不再有效:

\n
// @filename foo.mjs\nexport function foo() { ... }\n\n// @filename foo.test.mjs\nimport * as foo from \'./foo.mjs\'\n// Causes runtime error because named export "foo" is immutable\njest.spyOn(foo, \'foo\')\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x9d\x93使用 ESM 模块监视/模拟命名导出的当前“标准”方法是什么?

\n

潜在的解决方案

\n

委托给可变对象

\n
// @filename foo.mjs\nexport function foo() {\n  return _private.foo()\n}\nexport const _private = {\n  foo: () => { ... }\n}\n\n// @filename foo.test.mjs\nimport { _private } from \'./foo.mjs\'\njest.spyOn(_private, \'foo\')\n
Run Code Online (Sandbox Code Playgroud)\n

代理中的包装函数

\n
// @filename proxy.mjs\nexport function proxy(fn) {\n  const functionProxy = function (...args) {\n    return functionProxy._original(...args)\n  }\n  functionProxy._original = fn\n  return functionProxy\n}\n\n// @filename foo.mjs\nimport { proxy } from \'./proxy.mjs\'\nexport const foo = proxy(() => { ... })\n\n// @filename foo.test.mjs\nimport { foo } from \'./foo.mjs\'\njest.spyOn(foo, \'_original\')\n
Run Code Online (Sandbox Code Playgroud)\n

Tom*_*Tom 3

Jest 并不真正支持

模拟其他模块部分的标准方法是使用jest.mock,如下所示:

import * as foo from './foo.mjs'

jest.mock('./foo.mjs', () => ({
  foo: () => {
    console.log(`I AM FAKE FOO`)
    return 4
  }
}))
Run Code Online (Sandbox Code Playgroud)

在 CommonJS 环境中,jest.mock劫持该require函数,以便被测试代码加载的依赖项将根据需要替换为您的模拟。

不幸的是,Jest 尚未弄清楚如何在 ES6 模块环境中执行此操作。来自Jest 的嘲笑页面

请注意,我们目前在 ESM 中不以jest.mock干净的方式提供支持,但我们打算在未来添加适当的支持。请关注此问题以获取更新。

看起来底线是你将无法用 Jest 做你想做的事。


你说:

这个问题并不是 Jest 特有的,因为它适用于所有具有存根功能的测试库。

...但是,不喜欢,你的问题针对 Jest 的。像 Jest 这样的测试库通过做奇怪的事情来完成他们的工作,而不是通过利用现有的语言功能。这意味着您无法在此处了解它们都使用的一些神秘的 ES6 功能。他们每个人都使用一种发明的解决方案,据我所知,他们都是通过修改执行环境来工作的。