如何模拟ES6模块的导入? [英] How can I mock the imports of an ES6 module?
问题描述
我有以下ES6模块:
export function getDataFromServer() {
return ...
}
文件 widget.js
File widget.js
import { getDataFromServer } from 'network.js';
export class Widget() {
constructor() {
getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
render() {
...
}
}
我正在寻找一种使用 getDataFromServer
的模拟实例测试Widget的方法。如果我使用单独的< script>
s而不是ES6模块(例如在Karma中),则可以这样编写测试:
I'm looking for a way to test Widget with a mock instance of getDataFromServer
. If I used separate <script>
s instead of ES6 modules, like in Karma, I could write my test like:
describe("widget", function() {
it("should do stuff", function() {
let getDataFromServer = spyOn(window, "getDataFromServer").andReturn("mockData")
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
expect(otherStuff).toHaveHappened();
});
});
但是,如果我要在浏览器外部单独测试ES6模块(例如使用摩卡 + Babel ),我会写类似的东西:
However, if I'm testing ES6 modules individually outside of a browser (like with Mocha + Babel), I would write something like:
import { Widget } from 'widget.js';
describe("widget", function() {
it("should do stuff", function() {
let getDataFromServer = spyOn(?????) // How to mock?
.andReturn("mockData")
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
expect(otherStuff).toHaveHappened();
});
});
好,但是现在 getDataFromServer
在<$ c中不可用$ c> window (好吧,根本没有 window
),我不知道将东西直接注入<$ c的方法$ c> widget.js 自己的作用域。
Okay, but now getDataFromServer
is not available in window
(well, there's no window
at all), and I don't know a way to inject stuff directly into widget.js
's own scope.
- 是否可以访问
widget.js
的范围,或者至少用我自己的代码替换其导入内容? - 如果没有,如何使
小部件
可测试?
- Is there a way to access the scope of
widget.js
, or at least replace its imports with my own code? - If not, how can I make
Widget
testable?
我考虑过的东西
a。
从 widget.js
删除所有导入,并期望调用方提供dep。
Stuff I considered:
a. Manual dependency injection.
Remove all imports from widget.js
and expect the caller to provide the deps.
export class Widget() {
constructor(deps) {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
我很不喜欢这样破坏Widget的公共界面并暴露实施细节。
I'm very uncomfortable with messing up Widget's public interface like this and exposing implementation details. No go.
类似的东西:
import { getDataFromServer } from 'network.js';
export let deps = {
getDataFromServer
};
export class Widget() {
constructor() {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
然后:
import { Widget, deps } from 'widget.js';
describe("widget", function() {
it("should do stuff", function() {
let getDataFromServer = spyOn(deps.getDataFromServer) // !
.andReturn("mockData");
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
expect(otherStuff).toHaveHappened();
});
});
这种方法侵入性较小,但是需要我为每个模块编写很多样板,仍然存在着我一直使用 getDataFromServer
而不是 deps.getDataFromServer
。我对此感到不安,但这是到目前为止我最好的主意。
This is less invasive, but it requires me to write a lot of boilerplate for each module, and there's still a risk of me using getDataFromServer
instead of deps.getDataFromServer
all the time. I'm uneasy about it, but that's my best idea so far.
推荐答案
我已经开始使用<$ c $在我的测试中,c> import * as obj 样式,该样式将从模块中导入所有导出作为对象的属性,然后可以对其进行模拟。我发现这比使用诸如rewire或proxyquire或任何类似技术之类的方法要干净得多。例如,在需要模拟Redux操作时,我经常这样做。在上面的示例中,我可能会使用以下示例:
I've started employing the import * as obj
style within my tests, which imports all exports from a module as properties of an object which can then be mocked. I find this to be a lot cleaner than using something like rewire or proxyquire or any similar technique. I've done this most often when needing to mock Redux actions, for example. Here's what I might use for your example above:
import * as network from 'network.js';
describe("widget", function() {
it("should do stuff", function() {
let getDataFromServer = spyOn(network, "getDataFromServer").andReturn("mockData")
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
expect(otherStuff).toHaveHappened();
});
});
如果您的函数恰好是默认导出,则 import * as network从'./network'
会产生 {default:getDataFromServer}
,您可以模拟network.default。
If your function happens to be a default export, then import * as network from './network'
would produce {default: getDataFromServer}
and you can mock network.default.
这篇关于如何模拟ES6模块的导入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!