每次调用时将 sinon 中的函数存入 return 不同的值

Stubbing a function in sinon to return different value every time it is called

我有一个函数如下图:

function test(parms) {
        var self = this;
        return this.test2(parms)
        .then(function (data) {
            if (data) {
                return ;
            }
            else {
                return Bluebird.delay(1000)
                .then(self.test.bind(self, parms));
            }
        }.bind(self));
    };

我正在尝试为此功能编写单元测试。我正在使用 sinon.stub 模拟函数 test2 的功能。

我写了一个测试用例,其中 test2 returns true 因此 test 函数成功完成执行。但是我想要一个测试用例,其中第一个 test2 returns false,它等待延迟,下一次 test2 returns true。为此,我编写了如下测试用例:

var clock;
var result;
var test2stub;
var count = 0;

before(function () {
    clock = sinon.useFakeTimers();
    //object is defined before
    test2stub = sinon.stub(object,"test2", function () {
        console.log("Count is: " + count);
        if (count === 0) {
            return (Bluebird.resolve(false));
        }
        else if (count === 1) {
            return (Bluebird.resolve(true));
        }
    });
    clock.tick(1000);    
    object.test("xyz")
    .then(function (data) {
        result = data;
    });
    clock.tick(1000);
    count = count + 1;
    clock.tick(1000);
});

after(function () {
    test2stub.restore();
    clock.restore();
});

it("result should be undefined. Check if test2 returned false first & true next", 
  function () {
    expect(result).to.be.undefined;
});

在日志中它显示 count 的值仅为 0。

  1. test的代码其实是错误的。它从来没有 return 成功的数据。它 returns undefined。该函数应该 return 成功数据,否则您将无法将它用作下一个 .then 处理程序

    的参数
    .then(function (data) {
        if (data) {
            return data;
        }
    
  2. 接下来你对函数做出了错误的假设test。它永远不会 return undefined。该函数相当危险,它将在无尽的承诺链中永远调用自己,直到它从 test2 中挤出任何非空数据。

  3. 不应在 beforebeforeEach 部分启动测试代码。 beforeafter 是为了像伪造计时器一样准备环境然后恢复它们。
    it 处理程序中调用测试代码的一个原因是因为承诺应该以不同的方式处理。处理程序应接受一个参数,该参数指示测试将是异步的,并且测试引擎会为其提供超时(通常为 10 秒)以完成。预计测试将调用 done() 以指示测试成功或调用 done(error) 如果它失败并且存在 error 对象(或 expect 抛出异常)。
    您还应该在 异步操作开始后移动假计时器 。在您的代码中实际上第一个 clock.tick 是无用的。

  4. 使用 fakeTimers 有一个技巧。您可以手动移动时间,但它不会自行移动。对于第一个滴答声,它运作良好。承诺得到执行。然而,根据returning .delay(1000) 的承诺,将没有命令将时间向前推进。因此,要正确完成测试(而不是修改测试代码),您还必须存根 Bluebird.delay

我会更改存根实现并执行类似的操作

describe("test2", function(){
    beforeEach(function(){
        clock = sinon.useFakeTimers();
        test2stub = sinon.stub(object,"test2", function () {
            console.log("Count is: " + count);
            return (Bluebird.resolve((count++) > 0));
        });
        var _delay = Bluebird.delay.bind(Bluebird);
        bluebirdDelayStub = sinon.stub(Bluebird,"delay", function (delay) {
            var promise = _delay(delay);
            clock.tick(1000);
            return promise;
        });
    })
    it("should eventually return true", function (done) {
        object.test("xyz")
        .then(function (data) {
            expect(data).to.be.true;
            expect(count).to.equal(2);
            done();
        })
        .catch(function(err){
            done(err);
        });
        clock.tick(1000);
    });

    after(function () {
        test2stub.restore();
        clock.restore();        
        bluebirdDelayStub.restore();
    });    
})

PS 我在 Node.js 0.10.35 和 Bluebird 2.9.34

下验证了这段代码