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

4

使用 ES Modules,我们将一些导出的函数定义为 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函数调用。

有任何建议 - 如何在同一模块内真正地存根/模拟常量函数?


如果您展示了如何导入 myFs,那将非常有用。 - Daniel Kaplan
2个回答

2

const是常量,使用“正常”的代码无法更改它。不幸的是,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

0

我相信Sinon有一种方法可以做到这一点,而不需要使用第三方包(例如Babel)。这是他们的文档页面,其中包含一个简单的示例:如何存根模块的依赖项。我刚刚尝试了一下,似乎可以工作。

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


1
你只能模拟 Node.js 使用的 CommonJS 模块。这仍然不能让我们模拟静态的 ES6 模块。 - Harshal Patil
好的,如果在Node中执行测试文件之前,将测试文件和被测试模块Browserify到一个新的捆绑文件中,然后在Node中执行捆绑文件呢?这就是我现在正在做的事情,我能够使用ES6模块和Sinon依赖项存根方法来实现你所描述的功能。 - cag8f

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接