You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单元测试困惑:测试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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:36:03