如何为读取文件值的JavaScript对象编写Jest单元测试
问题根因
你遇到的报错是模块初始化时机和mock设置时机顺序颠倒导致的:
- Node.js 加载 CommonJS 模块时会立即执行模块顶层的所有代码,你业务代码里的
secretConfig对象是模块加载阶段就调用getSecret完成初始化的 - 你原测试代码先导入了
secretConfig模块,此时还没有给fs.readFileSync设置mock返回值,Jest mock的函数默认返回undefined,所以kafka_keystore_password属性在模块初始化时就被赋值为undefined,后续再设置mock返回值也不会更新这个已经初始化完成的对象,自然测试不通过。
解决方案1:调整测试代码(无需改动业务代码)
调整mock设置和模块导入的顺序,先设置好mock返回值再导入业务模块,同时用jest.resetModules()清除模块缓存,避免多个测试用例互相影响:
const fs = require('fs'); jest.mock('fs'); // 每次测试前清除模块缓存 beforeEach(() => { jest.resetModules(); }); describe('secret read files', () => { it('should read secret from file', () => { // 先设置mock返回值 fs.readFileSync.mockReturnValue('randomPrivateKey'); // 再导入业务模块,此时模块初始化会用到已经设置好的mock const secretConfig = require('./secret'); const secretMessage = secretConfig.kafka_keystore_password; expect(secretMessage).toEqual('randomPrivateKey'); // 额外验证调用路径符合预期 expect(fs.readFileSync).toHaveBeenCalledWith('/etc/secrets/kafka_keystore_password.txt', 'utf8'); }); });
解决方案2:优化业务代码(更易测试,支持惰性加载)
如果不想每次测试都动态导入模块,可以把业务代码里的secretConfig改成惰性加载的实现,只有实际访问属性时才会触发读取操作,不用再严格控制mock和导入的顺序:
// 改造后的业务代码导出部分 const secretConfig = { // 用getter实现惰性加载,访问属性时才执行读取 get kafka_keystore_password() { return getSecret('kafka_keystore_password'); } }; module.exports = secretConfig;
改造后你原来的测试代码只需要调换下导入和mock设置的顺序即可正常运行:
const fs = require('fs'); jest.mock('fs'); fs.readFileSync.mockReturnValue('randomPrivateKey'); // mock设置完成后再导入模块即可 const secretConfig = require('./secret'); describe('secret read files', () => { it('should read secret from file', () => { const secretMessage = secretConfig.kafka_keystore_password; expect(secretMessage).toEqual('randomPrivateKey'); }); });
内容的提问来源于stack exchange,提问作者amarnath harish
相关产品推荐
相关产品推荐

