在sinon中对函数进行存根,以便在每次调用时返回不同的值

时间:2015-07-24 00:13:12

标签: javascript node.js unit-testing 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));
    };

我正在尝试为此功能编写单元测试。我使用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;
});

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

1 个答案:

答案 0 :(得分:2)

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

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

  3. 我们不应该在beforebeforeEach部分启动测试代码。 beforeafter旨在为假冒计时器准备环境,然后进行恢复 在it处理程序中调用测试代码的一个原因是因为promises应该以不同方式处理。处理程序应接受一个参数,该参数指示测试将是异步的,并且测试引擎会给它一个超时(通常为10秒)来完成。测试期望调用done()以指示测试成功,或者调用done(error)如果失败并且有error个对象(或expect引发异常)。<登记/> 此外,您应该在异步操作开始后移动假计时器。实际上,在您的代码中,第一个clock.tick是无用的。

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

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

    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下验证了这段代码