Jest单元测试:如何对函数内部嵌套函数设置Mock响应
嘿,我刚好处理过类似的Mock场景!要给这个返回Promise的高阶函数里的嵌套依赖做Mock,用Jest(Node.js最常用的测试框架)就能轻松实现,我给你一步步拆解:
如何Mock Cassandra驱动模块中的嵌套函数
首先先把你的目标模块代码补全(方便大家理解逻辑):
// cassandraDriver.js const driver = require('cassandra-driver'); // 假设authProvider是外部模块或者内部嵌套函数 const authProvider = require('./auth-provider'); module.exports = ({ cassandra_user, cassandra_password, cassandra_address }) => { if (!cassandra_address.length) throw Error('Cassandra address is not valid'); return new Promise((resolve, reject) => { try { const client = new driver.Client({ contactPoints: cassandra_address.split(','), authProvider: authProvider(cassandra_user, cassandra_password) }); // 假设这里执行client.connect()并返回结果 client.connect() .then(() => resolve(client)) .catch(err => reject(err)); } catch (err) { reject(err); } }); };
方案1:Mock外部依赖(比如cassandra-driver和authProvider)
如果authProvider是外部模块,我们可以直接Mock整个依赖包:
// cassandraDriver.test.js const cassandraDriver = require('./cassandraDriver'); const driver = require('cassandra-driver'); // Mock cassandra-driver的Client类,以及它的connect方法 jest.mock('cassandra-driver', () => { // 先Mock connect方法,让它返回成功的Promise const mockConnect = jest.fn().mockResolvedValue(true); return { Client: jest.fn().mockImplementation(() => ({ connect: mockConnect })) }; }); // 再Mock authProvider模块,让它返回一个固定的mock值 jest.mock('./auth-provider', () => jest.fn().mockReturnValue('mock-auth-instance')); test('should resolve with client when config is valid', async () => { const testConfig = { cassandra_user: 'test-user', cassandra_password: 'test-pass', cassandra_address: '127.0.0.1,127.0.0.2' }; const clientInstance = await cassandraDriver(testConfig); // 验证Client是否被正确实例化,参数是否符合预期 expect(driver.Client).toHaveBeenCalledWith({ contactPoints: ['127.0.0.1', '127.0.0.2'], authProvider: 'mock-auth-instance' }); // 验证connect方法是否被调用 expect(clientInstance.connect).toHaveBeenCalled(); }); test('should throw error when cassandra_address is empty', () => { const invalidConfig = { cassandra_user: 'test-user', cassandra_password: 'test-pass', cassandra_address: '' }; expect(() => cassandraDriver(invalidConfig)).toThrow('Cassandra address is not valid'); });
方案2:Mock内部嵌套的authProvider函数
如果authProvider是cassandraDriver.js内部的私有函数,我们可以先把它导出(方便测试),再用jest.spyOn来Mock:
第一步:修改原模块,导出内部函数
// cassandraDriver.js const driver = require('cassandra-driver'); // 内部嵌套的authProvider函数 const authProvider = (user, pass) => { return new driver.auth.PlainTextAuthProvider(user, pass); }; // 导出这个函数,仅用于测试 module.exports.authProvider = authProvider; // 原有的主导出函数 module.exports = ({ cassandra_user, cassandra_password, cassandra_address }) => { // ... 原有逻辑不变 ... };
第二步:在测试中Mock内部函数
// cassandraDriver.test.js const cassandraDriver = require('./cassandraDriver'); const driver = require('cassandra-driver'); // 先Mock cassandra-driver的相关类 jest.mock('cassandra-driver', () => ({ Client: jest.fn().mockImplementation(() => ({ connect: jest.fn().mockResolvedValue(true) })), auth: { PlainTextAuthProvider: jest.fn() } })); test('should use authProvider with correct credentials', async () => { // 用spyOn来Mock内部的authProvider函数 const mockAuth = jest.spyOn(cassandraDriver, 'authProvider').mockReturnValue('mock-auth'); const testConfig = { cassandra_user: 'test-user', cassandra_password: 'test-pass', cassandra_address: '127.0.0.1' }; await cassandraDriver(testConfig); // 验证authProvider是否被传入正确的参数 expect(mockAuth).toHaveBeenCalledWith('test-user', 'test-pass'); // 验证Client实例化时用了mock的authProvider expect(driver.Client).toHaveBeenCalledWith(expect.objectContaining({ authProvider: 'mock-auth' })); // 测试结束后恢复原函数,避免影响其他测试 mockAuth.mockRestore(); });
几个关键提示
- 如果你用的是Sinon而不是Jest,思路完全一致:用
sinon.stub()替换目标依赖,设置返回值或行为即可。 - 测试错误场景时,别忘了用
await expect(cassandraDriver(invalidConfig)).rejects.toThrow(...)来验证Promise的reject情况。 - 一定要验证依赖的调用参数,而不只是测试返回结果,这样才能确保你的函数逻辑是符合预期的。
内容的提问来源于stack exchange,提问作者David Faizulaev
相关产品推荐
相关产品推荐

