在 sinon 中存根函数以在每次调用时返回不同的值

use*_*342 5 javascript unit-testing node.js sinon

我有一个功能,如下所示:

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));
    };
Run Code Online (Sandbox Code Playgroud)

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

我写了一个测试用例,其中test2返回true,因此test函数成功完成执行。但是我想要一个测试用例,在第一个实例test2返回时false,它等待延迟并且下次test2返回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;
});
Run Code Online (Sandbox Code Playgroud)

在日志中,它显示 count 只有值 0。

Kir*_*tin 3

  1. 的代码test实际上是错误的。它永远不会返回成功的数据。它返回undefined。该函数应在成功时返回数据,否则您将无法将其用作下一个.then处理程序的参数

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

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

  4. 使用时有一个技巧fakeTimers。您可以手动移动时间,但它不会自行移动。对于第一个刻度,效果很好。承诺已被执行。然而,在返回.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();
    });    
})
Run Code Online (Sandbox Code Playgroud)

PS 我在 Node.js 0.10.35 和 Bluebird 2.9.34 下验证了此代码