Jest Mock无法重置求助:测试用例相互干扰致失败
解决Jest中ES6导入下Mock模块重置失效的问题
这个问题的核心在于你直接替换了mock函数的引用,而不是修改已有mock的实现——这也是为什么jest.resetMocks()这类方法没生效的关键原因。
问题本质分析
当你用jest.mock('../../services/httpService')时,Jest会自动为HttpService生成一个mock对象,其中的post方法默认是一个空的jest.fn()实例。但你在第一个测试里做了这个操作:
HttpService.post = jest.fn(() => { return Promise.reject({ payload: 'rejected' }); });
这相当于把Jest最初创建的mock函数,直接替换成了一个全新的jest.fn()。后续的jest.resetMocks()只会对Jest原生生成的那个mock生效,自然无法影响你新创建的这个函数,导致测试用例之间互相污染。
正确的解决步骤
1. 修改已有mock的实现,而非替换引用
不要直接赋值HttpService.post,而是用Jest提供的mockImplementation方法修改已有mock的执行逻辑:
// 替换第一个测试里的赋值代码 HttpService.post.mockImplementation(() => Promise.reject({ payload: 'rejected' }));
这样你只是改变了原有mock函数的行为,并没有替换它的引用,后续的重置方法就能正常作用在这个mock上。
2. 确保每个测试后重置mock状态
在afterEach或beforeEach钩子中添加重置逻辑,保证每个测试用例启动时,mock都是干净的初始状态:
describe('async actions', () => { let store; let params; beforeEach(() => { store = mockStore({}); // 清除所有mock的调用记录,并重置实现为初始空函数 jest.resetAllMocks(); // 如果你只想清除调用记录、保留实现,用jest.clearAllMocks()即可 }); // ... 你的测试用例 });
jest.resetAllMocks():彻底重置所有mock,清除调用记录的同时,把mock恢复为初始的空函数。jest.clearAllMocks():仅清除mock的调用记录,保留之前设置的实现逻辑(适合需要复用基础实现的场景)。- 若只想针对单个mock重置,用
HttpService.post.mockReset()会更精准。
修改后的完整测试代码示例
import HttpService from '../../services/httpService'; import handleErrors from '../../utilities/handleErrors'; import constants from './path-to-constants'; // 补充你的constants路径 import actions from './path-to-actions'; // 补充你的actions路径 import mockStore from 'redux-mock-store'; jest.mock('../../services/httpService'); jest.mock('../../utilities/handleErrors'); const createMockStore = mockStore(); describe('async actions', () => { let store; let params; beforeEach(() => { store = createMockStore({}); jest.resetAllMocks(); }); describe('some describe that wraps both tests', () => { describe('a describe that wraps just the first test', () => { test(`creates ${constants.actions.REQUEST_SAVE_NOTE_FAILURE}`, () => { // 修改已有mock的实现 HttpService.post.mockImplementation(() => Promise.reject({ payload: 'rejected' }) ); const expectedActions = [ { type: constants.actions.REQUEST_SAVE_NOTE }, { type: constants.actions.REQUEST_SAVE_NOTE_FAILURE, data: { payload: 'rejected' } }, ]; return store.dispatch(actions.saveNote({ id: 1, note: 'note' })).then(() => { expect(store.getActions()).toEqual(expectedActions); }); }); }); describe('a describe that wraps just the second test', () => { test(`creates ${constants.actions.REQUEST_SAVE_NOTE} and ${constants.actions.RECEIVE_SAVE_NOTE}`, () => { params = { body: { prospects: [1], note: 'note', }, }; // 设置成功的mock实现 HttpService.post.mockImplementation(() => Promise.resolve({ payload: 'payload' }) ); const expectedActions = [ { type: constants.actions.REQUEST_SAVE_NOTE }, { type: constants.actions.RECEIVE_SAVE_NOTE, data: { payload: 'payload' } }, ]; return store.dispatch(actions.saveNote({ id: 1, note: 'note' })).then(() => { expect(store.getActions()).toEqual(expectedActions); expect(HttpService.post).toBeCalledWith({ ...params, url: '/api/prospect/add-note' }); }); }); }); }) });
关于ES6导入的补充说明
ES6模块的导出本身是只读的,但Jest的jest.mock会把整个模块的导出替换成一个可写的mock对象。不过直接替换mock对象的属性会打破Jest对mock的跟踪,所以始终用Jest提供的mockImplementation、mockReturnValue等方法修改行为,才是正确的姿势。
内容的提问来源于stack exchange,提问作者withintheruins14
相关产品推荐
相关产品推荐

