在 sinon.js 中使用 ES 模块时如何存根常量函数?

How to stub constant functions when using ES Modules with sinon.js?

对于 ES 模块,我们将一些导出函数定义为 const,这通常是一种标准做法。但是,在单元测试期间,这些 const 函数不能被删除,从单元测试的角度来看这似乎是有问题的。例如,

import * as fs from 'fs';

import { bindNodeCallback } from 'rxjs';
import { map } from 'rxjs/operators';

// readFile :: string, string => Observable<string>
export const readFile$ = bindNodeCallback(fs.readFile);

// readJson :: string => Observable<any>
export function readJson(fileName) {
    return readFile$(fileName, 'utf-8')
        .pipe(map((rawFile) => JSON.parse(rawFile)));
}

现在要对 readJson 进行单元测试,我通常希望存根 readFile$ 函数。不幸的是,以下 Sinon 代码不起作用:

// Setup data - stubs / mocks
const stub = sinon.stub(myFs, 'readFile$').returns(json$);

由于 Sinon 只是更改引用 myFs.readFile$,原始 const 仍指向原始函数,该函数又由 readJson 函数调用。

任何建议 - 我怎样才能真正 stub/mock 同一模块中的常量函数?

const 是常数,无法使用 "normal" 代码更改它。不幸的是 sinon 不是魔法。您需要检测您的代码以允许更改常量值。

假设您正在使用 babel 进行转译,您可以使用 babel-plugin-rewire.

将其添加到您的 babel 配置后,您将能够在您的测试代码中执行以下注入

import { default as readJson, __RewireAPI__ as rewire } from './path/to/readJson.js'

const stub = sinon.stub(myFs, 'readFile$').returns(json$)
rewire.__set__('readFile$', stub)

// readJson() would now call provided stub

我相信 Sinon 有办法做到这一点,无需使用第三方包(例如 Babel)。这是他们关于它的文档页面,有一个简单的例子:How to stub a dependency of a module。我自己试了一下,好像还行。

抱歉,我没有时间在回复中分享任何代码。我知道这是糟糕的形式 :-/ 但希望 link 能帮助一些人。