如何模拟ES6模块的进口? [英] How to mock the imports of an ES6 module?
问题描述
我有以下ES6模块:
network.js
export function getDataFromServer(){
return ...
}
widget.js
import {getDataFromServer} from'network.js ;
export class Widget(){
constructor(){
getDataFromServer(dataForWidget)
.then(data => this.render(data)) ;
}
render(){
...
}
}
我正在寻找一种方法来测试Widget的模拟实例 getDataFromServer
。如果我使用单独的< script>
而不是ES6模块,像在Karma,我可以写我的测试,如:
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模块(如Mocha + babel)我会写一些东西:
import'Widget} from'widget.js';
describe(widget,function(){
it(should do stuff,function(){
let getDataFromServer = spyOn(?????)/ /如何模拟
.andReturn(mockData)
让widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith(dataForWidget);
expect otherstuff).toHaveHappened();
});
});
好的,但现在 getDataFromServer
不可用在窗口
(好吧,根本没有$ code>窗口),我不知道一种直接注入东西的方法进入 widget.js
自己的范围。
那么我从哪里去?
- 有没有办法访问
widget.js
的范围,或至少替换使用我自己的代码导入? - 如果没有,我该如何使
Widget
testable? strong>
我认为:
a。手动依赖注入
从 widget.js
中删除所有导入,并期望调用者提供deps。 p>
export class Widget(){
constructor(deps){
deps.getDataFromServer(dataForWidget)
.then(data => this.render(data));
}
}
我很不舒服Widget的公共界面像这样并揭示实现细节。不去。
b。 从'network.js'导入{getDataFromServer};
export let deps = {
getDataFromServer
};
export class Widget(){
constructor(){
deps.getDataFromServer(dataForWidget)
.then(data => this.render ));
}
}
从'network.js'导入{getDataFromServer};
export let deps = {
getDataFromServer
};
export class Widget(){
constructor(){
deps.getDataFromServer(dataForWidget)
.then(data => this.render ));
}
}
然后:
import {Widget,deps} from'widget.js';
describe(widget,function(){
it(should do stuff,function(){
let getDataFromServer = spyOn(deps.getDataFromServer)//!
.andReturn(mockData);
让widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith(dataForWidget);
expect(otherStuff)totoHaveHappened ();
});
});
这是一个较少的侵入性,但要求我为每个模块编写大量的样板,还有一个所有的时间我使用 getDataFromServer
而不是 deps.getDataFromServer
的风险。我很不安,但这是迄今为止最好的想法。
我已经开始使用 import * as obj
样式在我的测试中,它导入从模块的所有导出作为可以被嘲笑的对象的属性。我发现这比使用诸如重新连线或代码或类似技术的东西要好很多。例如,当需要模拟Redux操作时,我最常做到这一点。以下是我上面可以使用的例子:
import * as'network.js';
describe(widget,function(){
it(should do stuff,function(){
let getFromServer = spyOn(network,getDataFromServer)。 andReturn(mockData)
let widget = new Widget();
expect(getDataFromServer).toHaveBeenCalledWith(dataForWidget);
expect(otherStuff).toHaveHappened();
});
});
如果您的功能恰好是默认导出,则 import *作为网络从'./network'
将产生 {default:getDataFromServer}
,你可以模拟network.default。
I have the following ES6 modules:
network.js
export function getDataFromServer() {
return ...
}
widget.js
import { getDataFromServer } from 'network.js';
export class Widget() {
constructor() {
getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
render() {
...
}
}
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();
});
});
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();
});
});
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.
So where do I go from here?
- 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?
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));
}
}
I'm very uncomfortable with messing up Widget's public interface like this and exposing implementation details. No go.
b. Expose the imports to allow mocking them.
Something like:
import { getDataFromServer } from 'network.js';
export let deps = {
getDataFromServer
};
export class Widget() {
constructor() {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
then:
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();
});
});
This is less invasive but 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.
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();
});
});
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屋!