Kos*_*Kos 133 javascript unit-testing mocha.js ecmascript-6
我有以下ES6模块:
network.js
export function getDataFromServer() {
return ...
}
Run Code Online (Sandbox Code Playgroud)
widget.js
import { getDataFromServer } from 'network.js';
export class Widget() {
constructor() {
getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
render() {
...
}
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找一种方法来测试Widget的模拟实例getDataFromServer
.如果我使用单独的<script>
s代替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();
});
});
Run Code Online (Sandbox Code Playgroud)
但是,如果我在浏览器之外单独测试ES6模块(比如使用Mocha + babel),我会写一些类似于:
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();
});
});
Run Code Online (Sandbox Code Playgroud)
好的,但现在getDataFromServer
没有window
(好吧,根本没有window
),我不知道将东西直接注入widget.js
自己的范围的方法.
widget.js
用我自己的代码访问范围,或至少替换它的导入?Widget
测试?删除所有导入widget.js
并期望调用者提供deps.
export class Widget() {
constructor(deps) {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
Run Code Online (Sandbox Code Playgroud)
我非常不喜欢弄乱Widget这样的公共界面并暴露实现细节.不行.
就像是:
import { getDataFromServer } from 'network.js';
export let deps = {
getDataFromServer
};
export class Widget() {
constructor() {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
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();
});
});
Run Code Online (Sandbox Code Playgroud)
这种侵入性较小,但要求我为每个模块编写大量样板文件,而且我仍然有使用getDataFromServer
而不是deps.getDataFromServer
一直使用的风险.我对此感到不安,但到目前为止,这是我最好的主意.
car*_*iam 121
我已经开始import * as obj
在我的测试中使用该样式,它将模块中的所有导出作为对象的属性导入,然后可以对其进行模拟.我发现这比使用重新布线或代理或任何类似技术更清洁.例如,当我需要模拟Redux动作时,我经常这样做.以下是我可能用于上述示例的内容:
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();
});
});
Run Code Online (Sandbox Code Playgroud)
如果您的函数恰好是默认导出,那么import * as network from './network'
就会生成{default: getDataFromServer}
并且您可以模拟network.default.
小智 28
@carpeliam是正确的但请注意,如果你想窥探模块中的一个函数并在该模块中使用另一个函数调用该函数,你需要将该函数作为exports命名空间的一部分调用,否则不会使用spy.
错误的例子:
// mymodule.js
export function myfunc2() {return 2;}
export function myfunc1() {return myfunc2();}
// tests.js
import * as mymodule
describe('tests', () => {
beforeEach(() => {
spyOn(mymodule, 'myfunc2').and.returnValue = 3;
});
it('calls myfunc2', () => {
let out = mymodule.myfunc1();
// out will still be 2
});
});
Run Code Online (Sandbox Code Playgroud)
正确的例子:
export function myfunc2() {return 2;}
export function myfunc1() {return exports.myfunc2();}
// tests.js
import * as mymodule
describe('tests', () => {
beforeEach(() => {
spyOn(mymodule, 'myfunc2').and.returnValue = 3;
});
it('calls myfunc2', () => {
let out = mymodule.myfunc1();
// out will be 3 which is what you expect
});
});
Run Code Online (Sandbox Code Playgroud)
@ vdloo的回答让我朝着正确的方向前进,但在同一个文件中同时使用commonjs"exports"和ES6模块"export"关键字对我来说不起作用(webpack v2抱怨).相反,我使用默认(命名变量)导出包装所有单个命名模块导出,然后在我的测试文件中导入默认导出.我正在使用以下导出设置与mocha/sinon和stubing工作正常,无需重新连接等:
// MyModule.js
let MyModule;
export function myfunc2() { return 2; }
export function myfunc1() { return MyModule.myfunc2(); }
export default MyModule = {
myfunc1,
myfunc2
}
// tests.js
import MyModule from './MyModule'
describe('MyModule', () => {
const sandbox = sinon.sandbox.create();
beforeEach(() => {
sandbox.stub(MyModule, 'myfunc2').returns(4);
});
afterEach(() => {
sandbox.restore();
});
it('myfunc1 is a proxy for myfunc2', () => {
expect(MyModule.myfunc1()).to.eql(4);
});
});
Run Code Online (Sandbox Code Playgroud)
我实现了一个库,它试图解决 TypeScript 类导入的运行时模拟问题,而无需原始类知道任何显式依赖注入。
该库使用import * as
语法,然后用存根类替换原始导出的对象。它保留了类型安全性,因此如果在没有更新相应测试的情况下更新了方法名称,您的测试将在编译时中断。
这个库可以在这里找到:ts-mock-imports。
归档时间: |
|
查看次数: |
54329 次 |
最近记录: |