Jest没有提升以"mock"为前缀声明的mock函数,我遇到了问题我的理解是,这应该根据笑话文档工作
我有一个redux操作,它用另一个依赖项做一些事情。调用依赖模块上的方法的结果将与另一个操作一起调度。
我如何在依赖模块AuthUtils
中模拟resume
的实现。调用thunk会抛出错误,因为resume
方法未定义
Actions.js
import { setUser } from '../../src/actions/UserActions';
import AuthUtils from '../utils/AuthUtils'; //dependent es6 class
const auth = new AuthUtils();
export const resumeSession = () => async (dispatch, getState) => {
try {
const resumeResult = await auth.resume(); // wait for result
dispatch(setUser(resumeResult)); //dispatch setUser with result
} catch() {
}
};
Actions.test.js:
import { resumeSession } from '../../src/actions/AuthActions';
import { setUser } from '../../src/actions/UserActions';
// auto mock UserActions
jest.mock('../../src/utils/UserActions');
// Mock resume method of AuthUtils using module factory param
// The mockResume here is undefined, but I expected because it begins with mock it would be hoisted along with the jest.mock call
// "An exception is made for variables that start with the word 'mock'." -- from the docks
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
jest.mock('../../src/utils/AuthUtils', () => {
return jest.fn().mockImplementation(() => {
return { resume: mockResume };
});
});
describe('resumeSession', () => {
it('dispatches complete', async () => {
const mockDispatch = jest.fn();
const mockGetState = jest.fn();
await resumeSession()(mockDispatch, mockGetState);
expect(setUser).toHaveBeenCalledWith({ user: { things } });
// Test blows up because AuthUtils#resume is not a function
});
});
在这种情况下,我敢肯定99%的问题是你嘲笑得太晚了。
const auth = new AuthUtils();
是模块文件中的内联代码。这意味着它在导入文件后立即执行。
您的测试文件按以下顺序运行代码:
import { resumeSession } from '../../src/actions/AuthActions';
// this does:
// import AuthUtils from '../utils/AuthUtils';
// const auth = new AuthUtils();
import { setUser } from '../../src/actions/UserActions';
jest.mock('../../src/utils/UserActions');
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
jest.mock('../../src/utils/AuthUtils', () => {
return jest.fn().mockImplementation(() => {
return { resume: mockResume };
});
});
// too late, since the code from the *actual* AuthUtils has already been executed
如果auth
是resumeSession
函数中的局部变量,则将正常工作,如下所示:
export const resumeSession = () => async (dispatch, getState) => {
const auth = new AuthUtils();
try {
const resumeResult = await auth.resume(); // wait for result
dispatch(setUser(resumeResult)); //dispatch setUser with result
} catch() {
}
};
因为mock是在任何代码尝试使用AuthUtils
之前设置的。但我认为您在函数之外创建auth
是有原因的。
如果不能将auth
的实例化移动到函数内部,则一种可能的解决方案是,在从AuthActions
:导入之前,将AuthUtils
及其resume
函数的mock和设置移动到
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
jest.mock('../../src/utils/AuthUtils', () => {
return jest.fn().mockImplementation(() => {
return { resume: mockResume };
});
});
import { resumeSession } from '../../src/actions/AuthActions';
import { setUser } from '../../src/actions/UserActions';
jest.mock('../../src/utils/UserActions');
如果这不起作用(或者如果您不希望在导入之前没有任何代码(,另一种选择是导出auth
变量,这样您就可以监视实际实例并模拟其resume
函数:
import { auth, resumeSession } from '../../src/actions/AuthActions';
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
jest.spyOn(auth, "resume").mockImplementation(mockResume);
这个可能会产生副作用,在这个测试完成后,让你的模拟实现继续用于其他测试,这可能是你不想要的。您可以使用Jest的生命周期方法来避免这种情况,并在测试完成后恢复原始的resume
实现:
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
const resumeSpy = jest.spyOn(auth, "resume");
resumeSpy.mockImplementation(mockResume);
describe('resumeSession', () => {
afterAll(() => {
resumeSpy.mockRestore();
});
it('dispatches complete', async () => {
const mockDispatch = jest.fn();
const mockGetState = jest.fn();
await resumeSession()(mockDispatch, mockGetState);
expect(setUser).toHaveBeenCalledWith({ user: { things } });
});
});
不相关的附带说明:Jest mock函数(和间谍(有一个方便的函数来模拟Promise结果,因此您不需要手动调用
Promise.resolve()
或Promise.reject()
的mock实现。我个人更喜欢使用Jest自己的功能:const mockResume = jest.fn(); mockResume.mockResolvedValue({ user: { things } }));
如果你使用间谍方法,你可以完全放弃
mockResume
功能:const resumeSpy = jest.spyOn(auth, "resume"); resumeSpy.mockResolvedValue({ user: { things } }));
这与你目前的问题无关,但我想我会把它扔掉。