单元测试困惑:测试processHash时该用Sinon Stub还是Fake?
Sinon Stub与Fake的选择:测试processHash时如何替代processBatch
我刚接触单元测试,正在用Chai+Mocha和Sinon编写单元测试。现有两个函数processHash和processBatch,其中processHash会内部调用processBatch,且我已为processBatch编写了测试用例,因此测试processHash时无需重复验证processBatch的逻辑。
最初我计划使用Stub来手动模拟processBatch的返回结果(由于processBatch内部会与Redis缓存、SQL数据库等第三方服务交互),但查阅Sinon Fake的文档后,发现Fake似乎也适用于该场景,因此困惑于二者该如何选择。
相关函数代码
const processBatch = async (hashKeys, timestamp) => { // Get data from Redis Hash const [detailsArr, detailsErr] = await cacheService.getMultipleFieldsFromHash( 'user_details', hashKeys ); //DB insertion const [insertSuccess, insertError] = await dbService.insertLogs( detailsArr, timestamp ); hashKeys = []; return [true, null]; }; const processHash = async (key, timestamp, currentTimestamp) => { //get hashKeys from sorted set const [hashKeys, hashKeysError] = await cacheService.getDataInRange( key, 0, -1 ); const batchSize = BATCH_SIZE; const totalKeys = hashKeys.length; for (let i = 0; i < totalKeys; i += batchSize) { const batch = hashKeys.slice(i, i + batchSize); const [success, error] = await processBatch(batch, timestamp); } return [true, null]; };
processBatch的测试用例
describe('processBatch function', () => { it('should return success=true if it executes correctly', async () => { cacheServiceStub = sinon.stub(); dbServiceStub = sinon.stub(); cacheServiceStub.getMultipleFieldsFromHash.resolves([[], null]); dbServiceStub.insertLogs().resolves([true, null]); const [success, err] = await processBatch(hashKeys, timestamp); expect(success).to.be.a('boolean'); expect(success).to.be.equal(true); expect(err).to.be.null; }); });
核心区别与选择建议
1. Stub的适用场景
Stub是Sinon中最常用的依赖替换工具,专门用来替换目标函数/方法,预设返回值或行为,完全隔离原函数的执行逻辑。对你的场景来说:
- 你已经验证过
processBatch的正确性,测试processHash只需要确保它能按预期调用processBatch(比如调用次数、传入的分批参数是否正确),无需关心processBatch内部执行。 - 用Stub可以直接预设
processBatch的返回值,还能通过calledWith、callCount等断言验证processHash的分批逻辑是否符合预期。
2. Fake的适用场景
Fake更偏向于创建轻量的自定义假实现,可以包含简单的业务逻辑,而不是单纯预设返回值。比如需要让假函数记录调用参数、执行极简计算时,Fake会更合适,但如果只是需要替换原函数并固定返回值,Fake就显得多余。
3. 针对你的场景:优先选择Stub
你的需求非常明确:完全跳过processBatch的真实执行,只验证processHash的调用逻辑,同时用预设返回值保证流程走通。用Stub更直接高效,代码示例如下:
describe('processHash function', () => { it('should split hashKeys into batches and call processBatch correctly', async () => { // 模拟cache返回测试用的hashKeys sinon.stub(cacheService, 'getDataInRange').resolves([['key1','key2','key3','key4'], null]); // 替换processBatch为Stub,预设返回成功结果 const processBatchStub = sinon.stub(processBatch).resolves([true, null]); await processHash('test_key', 123456, 789012); // 验证调用次数(假设BATCH_SIZE为2,应调用2次) expect(processBatchStub.callCount).to.equal(2); // 验证每次传入的分批参数是否正确 expect(processBatchStub.firstCall.args[0]).to.deep.equal(['key1','key2']); expect(processBatchStub.secondCall.args[0]).to.deep.equal(['key3','key4']); // 恢复Stub,避免影响其他测试 cacheService.getDataInRange.restore(); processBatchStub.restore(); }); });
总结:Stub完全匹配你"替换依赖、预设行为、验证调用"的核心需求;Fake适合需要自定义简单逻辑的场景,当前场景下无需使用。
内容的提问来源于stack exchange,提问作者Your Devmate
相关产品推荐
相关产品推荐

