如何在Jest中按测试用例修改lru-cache构造函数返回对象实现
解决Jest中lru-cache的get方法Mock实现不生效问题
问题场景
项目中使用lru-cache的方式如下:
const LRU = require('lru-cache'); const cache = new LRU({ max: 500, ttl: 1000 * 60 * 15, updateAgeOnGet: false, updateAgeOnHas: false, }); // 被测试函数依赖该cache实例 function myFunction(key) { return cache.get(key); }
尝试用Jest模拟lru-cache时,在测试用例中修改get方法的mock实现始终不生效,示例代码:
const LRU = require('lru-cache'); jest.mock('lru-cache'); it('should return custom response', () => { LRU.mockImplementation(() => ({ get: jest.fn().mockImplementation(data => ({ response: data })), set: jest.fn() })); // 调用被测试函数时,get方法仍为默认mock,自定义实现未生效 const result = myFunction('test-key'); });
但直接在测试文件内修改已实例化的cache.get却能生效:
cache.get.mockImplementation(identifier => ({ response: identifier }));
问题原因
被测试模块在导入阶段就已经执行new LRU()创建了缓存实例,而Jest的模块mock是在模块加载时替换的。如果在测试用例中才修改LRU.mockImplementation,此时被测试模块已经持有了之前的mock实例,新的mock实现无法作用到已创建的实例上。
解决方案
方案1:重置模块并在测试用例中重新导入被测试模块
使用jest.resetModules()在每个测试前重置模块缓存,先修改mock实现,再导入被测试模块,确保模块使用最新的mock构造函数:
jest.mock('lru-cache'); const LRU = require('lru-cache'); describe('myFunction', () => { beforeEach(() => { jest.resetModules(); jest.clearAllMocks(); }); it('should return custom response', () => { // 先设置LRU的mock实现 LRU.mockImplementation(() => ({ get: jest.fn().mockImplementation(data => ({ response: data })), set: jest.fn() })); // 再导入被测试模块 const { myFunction } = require('./path-to-your-module'); const result = myFunction('test-key'); expect(result).toEqual({ response: 'test-key' }); expect(LRU).toHaveBeenCalledTimes(1); }); it('should return fixed value in another test', () => { LRU.mockImplementation(() => ({ get: jest.fn().mockReturnValue('fixed-value'), set: jest.fn() })); const { myFunction } = require('./path-to-your-module'); const result = myFunction('any-key'); expect(result).toBe('fixed-value'); }); });
方案2:让mock返回可复用的mock函数
提前定义get和set为可修改的jest mock函数,无需重新mock构造函数,直接在测试用例中修改get的实现:
const LRU = require('lru-cache'); // 定义可复用的mock函数 const mockGet = jest.fn(); const mockSet = jest.fn(); jest.mock('lru-cache', () => { return jest.fn(() => ({ get: mockGet, set: mockSet })); }); // 导入被测试模块 const { myFunction } = require('./path-to-your-module'); describe('myFunction', () => { beforeEach(() => { jest.clearAllMocks(); }); it('should return custom response', () => { mockGet.mockImplementation(data => ({ response: data })); const result = myFunction('test-key'); expect(result).toEqual({ response: 'test-key' }); expect(mockGet).toHaveBeenCalledWith('test-key'); }); it('should return null when key not found', () => { mockGet.mockReturnValue(null); const result = myFunction('non-existent-key'); expect(result).toBeNull(); }); });
方案3:使用jest.spyOn模拟get方法
若无需mock整个lru-cache模块,仅需模拟get方法,可直接用jest.spyOn替换实例上的方法:
// 导入被测试模块和cache实例 const { myFunction, cache } = require('./path-to-your-module'); describe('myFunction', () => { it('should return custom response', () => { const spy = jest.spyOn(cache, 'get').mockImplementation(data => ({ response: data })); const result = myFunction('test-key'); expect(result).toEqual({ response: 'test-key' }); // 测试完成后恢复原方法 spy.mockRestore(); }); });
内容的提问来源于stack exchange,提问作者Tanner Summers
相关产品推荐
相关产品推荐

