Jest Mock实现失效:调用原始数据库函数而非模拟函数
解决Jest Mock数据库addTrip函数失效的问题
问题描述
测试API时尝试Mock数据库的addTrip函数,但实际执行的是原始函数,尽管expect(createTripMock).toHaveBeenCalled()未报错,仍需要实现仅调用模拟函数并返回预设结果,不执行原始逻辑。
核心原因
你的测试代码中,app模块在Mock之前就已经导入,此时app内部已经引用了原始的addTrip函数,后续通过jest.spyOn修改tripDb的addTrip不会影响app里已存在的引用,导致Mock失效。
解决方案
方案1:调整模块导入顺序,先Mock再导入app
将app和supertest的导入放在Mock之后,确保app加载时使用的是Mock后的addTrip函数:
const supertest = require('supertest'); const axios = require('axios'); const querystring = require('querystring'); const DEF = require('../Definition'); const tripDb = require('../database/trip'); const { logger } = require('../Log'); // 先执行Mock操作 const createTripMock = jest.spyOn(tripDb, 'addTrip').mockImplementation(() => Promise.resolve({ pk: `${companyId}_trip`, uid: '1667561135293773', lsi1: 'Kotha Yatra', lsi2: companyId, name: 'Kotha Yatra', companyId, origin: { address: 'Goa, India', location: { lat: 15.2993265, lng: 74.12399599999999, }, }, destination: { address: 'Norway', location: { lat: 60.47202399999999, lng: 8.468945999999999, }, }, path: [ { lat: 15.2993265, lng: 74.12399599999999, }, { lat: 60.47202399999999, lng: 8.468945999999999, }, ], isDeleted: false, currentVersion: 1, geofences: [], })); // 再导入app并创建request实例 const { app } = require('../app'); const request = supertest.agent(app); describe('trips route test', () => { // 剩余测试代码保持不变 });
方案2:使用jest.mock直接Mock整个数据库模块
这种方式更可靠,能确保所有模块引用的都是Mock后的函数:
在测试文件开头添加全局Mock:
jest.mock('../database/trip', () => ({ addTrip: jest.fn().mockResolvedValue({ pk: `${companyId}_trip`, uid: '1667561135293773', lsi1: 'Kotha Yatra', lsi2: companyId, name: 'Kotha Yatra', companyId, origin: { address: 'Goa, India', location: { lat: 15.2993265, lng: 74.12399599999999, }, }, destination: { address: 'Norway', location: { lat: 60.47202399999999, lng: 8.468945999999999, }, }, path: [ { lat: 15.2993265, lng: 74.12399599999999, }, { lat: 60.47202399999999, lng: 8.468945999999999, }, ], isDeleted: false, currentVersion: 1, geofences: [], }) }));
在测试用例中可随时修改Mock行为或断言调用:
const tripDb = require('../database/trip'); test('post correct data', async (done) => { // 如需临时修改返回值 tripDb.addTrip.mockResolvedValue({ /* 自定义返回数据 */ }); const response = await request.post('/api/trips') .set('Authorization', `Bearer ${token}`) .send(tripPayload); expect(tripDb.addTrip).toHaveBeenCalled(); expect(response.status).toEqual(200); expect(response.body.status).toBe('success'); done(); });
方案3:测试后清除Mock状态
在每个测试用例结束后重置Mock,避免污染其他测试:
afterEach(() => { // 清除所有Mock的调用记录和实例 jest.clearAllMocks(); }); // 或针对特定Mock重置 afterEach(() => { tripDb.addTrip.mockReset(); });
关键注意点
- Mock操作必须发生在所有依赖该模块的其他模块(如
app)导入之前,否则已加载的模块会保留原始函数引用。 jest.mock比jest.spyOn更适合全局Mock模块,能彻底避免导入顺序问题。
内容的提问来源于stack exchange,提问作者Prasanth Perli
相关产品推荐
相关产品推荐

