Ecmascript 6 如何用jest模拟redux操作依赖性的实现
我在Jest中遇到了问题,因为它没有提升前缀为“mock”的mock函数 我的理解是,这应该按照 我有一个redux操作,它使用另一个依赖项执行某些操作。 然后,对依赖模块调用方法的结果将与另一个操作一起调度 如何在从属模块Ecmascript 6 如何用jest模拟redux操作依赖性的实现,ecmascript-6,redux,jestjs,Ecmascript 6,Redux,Jestjs,我在Jest中遇到了问题,因为它没有提升前缀为“mock”的mock函数 我的理解是,这应该按照 我有一个redux操作,它使用另一个依赖项执行某些操作。 然后,对依赖模块调用方法的结果将与另一个操作一起调度 如何在从属模块AuthUtils中模拟resume的实现。调用thunk会引发错误,因为resume方法未定义 Actions.js import { setUser } from '../../src/actions/UserActions'; import AuthUtils from
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() {
}
};
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
});
});
Actions.test.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() {
}
};
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() {
}
};
因为模拟是在任何代码尝试使用AuthUtils
之前设置的。但是我假设您在函数外部创建auth
是有原因的
如果将auth
的实例化移动到函数内部不是一个选项,一个可能的解决方案是将AuthUtils
及其resume
函数的模拟和设置移动到从authoctions
导入之前:
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模拟函数(和SPIE)有一个方便的函数来模拟承诺结果,因此您不需要手动调用Promise.resolve()
或Promise.reject()
的模拟实现。我个人更喜欢使用Jest自己的功能:
const mockResume = jest.fn();
mockResume.mockResolvedValue({ user: { things } }));
如果您使用spy方法,您可以同时删除mockResume
功能:
const resumeSpy = jest.spyOn(auth, "resume");
resumeSpy.mockResolvedValue({ user: { things } }));
这与你目前遇到的问题无关,但我想我会把它扔出去
绝对正确,我将AuthUtils类的初始化移到resume方法中,然后低看,一切都按预期进行。我把它放在方法之外,因为有更多的操作,它们只是共享同一个实例,但是每次初始化一个新实例并没有真正的缺点。一些非常好的建议,非常感谢你的回答,瑞克