假设我有一个导出默认函数的简单文件:
// UniqueIdGenerator.js
const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
export default uniqueIdGenerator;
我会这样使用:
import uniqueIdGenerator from './UniqueIdGenerator';
// ...
uniqueIdGenerator();
我想在我的测试中断言调用此方法时会保留原始功能。我会使用
jest.spyOn
来做到这一点,但是它需要一个对象以及一个函数名称作为参数。如何以干净的方式做到这一点?对于任何感兴趣的人来说,jasmine
有一个类似的GitHub问题。
我最终放弃了默认导出:
// UniqueIdGenerator.js
export const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
然后我可以像这样使用和监视它:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'uniqueIdGenerator');
有些人建议将它们包装在 const 对象中,然后将其导出。我想你也可以使用类来包装。
但是,如果您无法修改类,仍然有一个(不太好的)解决方案:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'default');
这是一种默认导出的方法,无需修改导入(甚至在测试中根本不需要导入):
const actual = jest.requireActual("./UniqueIdGenerator");
const spy = jest.spyOn(actual, "default");
还可以模拟导入并将原始实现作为模拟实现传递,例如:
import uniqueIdGenerator from './UniqueIdGenerator'; // this import is a mock already
jest.mock('./UniqueIdGenerator.js', () => {
const original = jest.requireActual('./UniqueIdGenerator')
return {
__esModule: true,
default: jest.fn(original.default)
}
})
test(() => {
expect(uniqueIdGenerator).toHaveBeenCalled()
})
在某些情况下,您必须模拟导入才能监视默认导出:
import * as fetch from 'node-fetch'
jest.mock('node-fetch', () => ({
default: jest.fn(),
}))
jest.spyOn(fetch, 'default')
使用“default”作为spyOn函数中的第二个参数。
import * as MyHelperMethod from '../myHelperMethod';
jest.spyOn(MyHelperMethod, 'default');
仅模拟默认导出或任何其他导出,但将模块中的剩余导出保留为原始导出:
import myDefault, { myFunc, notMocked } from "./myModule";
jest.mock("./myModule", () => {
const original = jest.requireActual("./myModule");
return {
__esModule: true,
...original,
default: jest.fn(),
myFunc: jest.fn()
}
});
describe('my description', () => {
it('my test', () => {
myFunc();
myDefault();
expect(myFunct).toHaveBeenCalled();
expect(myDefault).toHaveBeenCalled();
myDefault.mockImplementation(() => 5);
expect(myDefault()).toBe(5);
expect(notMocked()).toBe("i'm not mocked!");
})
});
对我有用的是Janne Annala的答案和OP自己的解决方案的结合。我想要测试的只是使用正确的参数调用辅助方法,因为我已经为辅助方法编写了测试,并且它对我后续的测试没有任何影响:
// myHelperMethod.js
export const myHelperMethod = (param1, param2) => { // do something with the params };
// someOtherFileUsingMyHelperMethod.js
import * as MyHelperMethod from '../myHelperMethod';
jest.mock('../myHelperMethod', () => ({
myHelperMethod: jest.fn(),
}));
let myHelperMethodSpy = jest.spyOn(MyHelperMethod, 'myHelperMethod');
// ...
// some setup
// ...
test(() => {
expect(myHelperMethodSpy).toHaveBeenCalledWith(param1, param2);
});
这里就更简单了。
使用
sleep
模拟导出的模块“addDelay”(其中包含 jest
函数)。
const { sleep } = require('../../src/utils/addDelay');
jest.mock('../../src/utils/addDelay', () => {
const delay = jest.requireActual('../../src/utils/addDelay');
return {
...delay,
sleep: jest.fn(),
};});
测试如下,检查
sleep
函数是否像 arg 中那样在 1 秒内调用。
test("Should delay 1 second if Okta user has no IDxM Roles", async () => {
// GIVEN
const MockSleep = sleep;
// WHEN
await getUser(req, res);
// THEN
expect(MockSleep).toHaveBeenCalledWith(1000);// sleep(1000): 1sec
});
我知道我参加聚会迟到了,但我最近遇到了这个问题,也想分享我的解决方案......虽然它看起来有点不传统,但可以由有更多知识的人进行调整。
我碰巧有一个具有我想要监视的功能的文件。
// /foo/ModuleToBeMocked.ts
const fnToSpyOn = () => ...;
export default { fnToSpyOn }
然后将其导入到父文件中,该文件将带来和导出类似的功能。有点像分类。
// /parent.ts
import fnToSpyOn from './foo/ModuleToBeMocked';
import someOtherFn from './foo/SomeOtherModule';
...
export { fnToSpyOn, someOtherFn, ... };
这就是我测试
fnToSpyOn
的方式
// /foo/ModuleToBeMocked.test.ts
import { ModuleToBeMocked } from '../parent';
const fnToSpyOnSpu = jest.spyOn(ModuleToBeMocked, 'fnToSpyOn');
用户的解决方案thisismydesign(第一个答案)对我不起作用。我做了一些修改才能开始工作。
首先,我导出了一个默认对象:
// UniqueIdGenerator.js
const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
export default {uniqueIdGenerator}
为了使用它,我做了如下操作:
import generator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(generator, 'uniqueIdGenerator');