我想测试我的一个ES6模块以特定的方式调用另一个ES6模块。有了茉莉,这是非常容易的--
应用程序代码:
// myModule.js
import dependency from './dependency';
export default (x) => {
dependency.doSomething(x * 2);
}
和测试代码:
//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';
describe('myModule', () => {
it('calls the dependency with double the input', () => {
spyOn(dependency, 'doSomething');
myModule(2);
expect(dependency.doSomething).toHaveBeenCalledWith(4);
});
});
什么是笑话?我觉得这是一件很简单的事情,但我一直在努力想弄明白。
最接近的方法是用require
s替换import
s,并将它们移动到测试/函数中。这两件事都不是我想做的。
// myModule.js
export default (x) => {
const dependency = require('./dependency'); // Yuck
dependency.doSomething(x * 2);
}
//myModule-test.js
describe('myModule', () => {
it('calls the dependency with double the input', () => {
jest.mock('../dependency');
myModule(2);
const dependency = require('../dependency'); // Also yuck
expect(dependency.doSomething).toBeCalledWith(4);
});
});
如果dependency.js
中的函数是默认导出,我希望整个过程都能正常工作。然而,我知道在Jasmine中刺探默认导出不起作用(或者至少我永远无法让它起作用),所以我也不抱希望在开玩笑的时候这是可能的。
我已经能够通过使用涉及import*
的黑客来解决这个问题。它甚至同时适用于命名导出和默认导出!
对于命名导出:
// dependency.js
export const doSomething = (y) => console.log(y)
// myModule.js
import { doSomething } from './dependency';
export default (x) => {
doSomething(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';
describe('myModule', () => {
it('calls the dependency with double the input', () => {
dependency.doSomething = jest.fn(); // Mutate the named export
myModule(2);
expect(dependency.doSomething).toBeCalledWith(4);
});
});
或默认导出:
// dependency.js
export default (y) => console.log(y)
// myModule.js
import dependency from './dependency'; // Note lack of curlies
export default (x) => {
dependency(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';
describe('myModule', () => {
it('calls the dependency with double the input', () => {
dependency.default = jest.fn(); // Mutate the default export
myModule(2);
expect(dependency.default).toBeCalledWith(4); // Assert against the default
});
});
正如Mihai Damian在下面正确指出的,这是在改变dependency
的模块对象,因此它将“泄漏”到其他测试中。因此,如果您使用这种方法,您应该存储原始值,然后在每次测试后重新设置它。
要使用Jest轻松地完成此操作,请使用spyOn()方法而不是Jest.fn()
,因为它支持轻松地恢复其原始值,从而避免前面提到的“泄漏”。
你得模仿模块,自己设置间谍:
import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
doSomething: jest.fn()
}))
describe('myModule', () => {
it('calls the dependency with double the input', () => {
myModule(2);
expect(dependency.doSomething).toBeCalledWith(4);
});
});
要使用jest模拟ES6依赖项模块默认导出:
import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');
// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);
describe('myModule', () => {
it('calls the dependency once with double the input', () => {
myModule(2);
expect(dependency).toHaveBeenCalledTimes(1);
expect(dependency).toHaveBeenCalledWith(4);
});
});
其他选择对我的案子都不起作用。