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

Sinon Mock函数无法多次调用,报错:匿名Mock已被调用一次

问题分析与解决

核心原因

你遇到的ExpectationError: Anonymous mock already called once错误,本质是Sinon匿名mock的默认期望是单次调用,但async.retry会调用getList两次(第一次抛错触发重试,第二次成功),超出了mock期望的调用次数限制。另外你代码里存在变量名大小写笔误(getLisMock vs GetListMock),这也会导致逻辑异常。

解决方案

1. 修正变量名笔误

先把所有变量名统一,比如都用getListMock:

let getListMock = sinon.mock();

2. 调整mock的调用次数期望

不要直接用calledWith,而是通过exactly(n)或atLeast(n)明确允许多次调用:

// 明确期望getList被调用2次,每次参数都是{data: 'data'}
getListMock.calledWith({data: 'data'}).exactly(2);

3. 正确设置mock的调用行为

结合onCall设置每次调用的返回:

getListMock.onCall(0).yields(new Error('error'), null);
getListMock.onCall(1).yields(null, {items: [{id: 1}]});

4. 完整测试流程示例

const sinon = require('sinon');
const rewire = require('rewire');
const async = require('async');
const assert = require('assert');

// 引入待测试模块
const someModule = rewire('./your-module-path');

describe('someFunc', () => {
  it('should retry getList on failure', (done) => {
    // 创建mock并设置期望
    let getListMock = sinon.mock();
    getListMock.calledWith({data: 'data'}).exactly(2);
    
    // 设置两次调用的行为
    getListMock.onCall(0).yields(new Error('error'), null);
    getListMock.onCall(1).yields(null, {items: [{id: 1}]});

    // 用rewire替换getList
    someModule.__set__('getList', getListMock);

    // 调用待测试函数
    someModule.someFunc({data: 'data'}, (err, result) => {
      try {
        // 验证mock期望是否满足
        getListMock.verify();
        // 断言结果正确
        assert.deepEqual(result, {items: [{id: 1}]});
        done();
      } catch (e) {
        done(e);
      }
    });
  });
});

额外说明

如果你不确定具体调用次数,也可以用atLeast(1)来允许至少一次调用,但这里因为async.retry的times:3,最多会调用3次(如果前两次都失败),所以根据你的实际逻辑调整次数即可。

内容的提问来源于stack exchange,提问作者Froggy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:35:26