如何用jest模拟redux操作依赖性的实现



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

如果authresumeSession函数中的局部变量,则将正常工作,如下所示:

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 } }));

这与你目前的问题无关,但我想我会把它扔掉。

最新更新