在没有 babel 的情况下存根从原生 ES 模块导出

Stub an export from a native ES Module without babel

我正在使用 AVA + sinon 构建我的单元测试。因为我需要 ES6 模块而且我不喜欢 babel,所以我在整个项目中都使用了 mjs 文件,包括测试文件。我使用“--experimental-modules”参数来启动我的项目,并在测试中使用 "esm" 包。以下是我的ava配置和测试代码

  "ava": {
    "require": [
      "esm"
    ],
    "babel": false,
    "extensions": [
      "mjs"
    ]
  },


// test.mjs
import test from 'ava';
import sinon from 'sinon';
import { receiver } from '../src/receiver';
import * as factory from '../src/factory';

test('pipeline get called', async t => {
  const stub_factory = sinon.stub(factory, 'backbone_factory');
  t.pass();
});

但我收到错误消息:

  TypeError {
    message: 'ES Modules cannot be stubbed',
  }

如何在没有 babel 的情况下存根 ES6 模块?

According to John-David Dalton, the creator of the esm package,只能更改 *.js 个文件的命名空间 - *.mjs 个文件 已锁定 .

这意味着 Sinon(和所有其他软件)无法存根这些模块 - 正如错误消息所指出的那样。这里有两种方法可以解决此问题:

  1. 只需将文件的扩展名重命名为 .js 即可使导出可变。这是侵入性最小的,因为 mutableNamespace 选项默认为 on esm。当然,这仅在您使用 esm 加载器时适用。
  2. 使用代理所有导入的专用模块加载器,并根据您的喜好替换它们。

选项 2 的技术堆栈无关术语是 link seam - essentially replacing Node's default module loader. Usually one could use Quibble, ESMockproxyquirerewire,这意味着使用 Proxyquire 时上面的测试看起来像这样:

// assuming that `receiver` uses `factory` internally

// comment out the import - we'll use proxyquire
// import * as factory from '../src/factory';
// import { receiver } from '../src/receiver';

const factory = { backbone_factory: sinon.stub() };
const receiver = proxyquire('../src/receiver', { './factory' : factory });

修改 proxyquire 示例以使用 Quibble 或 ESMock(均原生支持 ESM)应该是微不足道的。

诗乃需要与时俱进,否则就会落伍(ESM is becoming defacto now with Node 12),因为它的许多局限性导致使用起来非常痛苦。

This article 提供了一个解决方法(实际上是 4 个,但我只发现 1 个是可以接受的)。就我而言,我直接从模块导出函数并收到此错误:ES Modules cannot be stubbed

export function abc() {

}

解决方案是将函数放入 class 并改为导出:

export class Utils {
  abc() {
  
  }
}

请注意,方法语法中的 function 关键字已删除。

Happy Coding - 希望 Sinon 长篇大论 运行,但它看起来不太好,因为它过于僵硬。