在sinon中断言递归调用的数量

时间:2015-07-23 10:58:56

标签: javascript node.js mocha sinon

我有以下队列使用者类,它通过promises递送运行:

"use strict";

var queue = require("./queue"),
    helpers = require("./helpers"),
    vendors = require("../config/vendors"),
    queueConf = require("../config/queue");

function Consumer() {
    this.queue =  new queue.TaskQueue();
    this.currentItem = null;
    this.port = null;
    this.payload = null;
}

Consumer.prototype.payloadSuccessCb = function (data) {
    this.payload = data;
    this.run();
};

Consumer.prototype.failureCb = function (data) {
    console.error(data);
    throw new Error(data);
    //TODO: Continue queue processing despite the error
};

Consumer.prototype.processItem = function (data) {
    this.currentItem = data;
    process.send("Proccess " +  process.pid + " is processing item " + this.currentItem);
    helpers.getPayload(this.currentItem).then(this.payloadSuccessCb, this.failureCb);
};

Consumer.prototype.wait = function () {
    var self = this;
    process.send("Proccess " + process.pid + " is waiting for new items");
    setTimeout(function () {
        self.run();
    }, queueConf.waitTime);
};

Consumer.prototype.queueSuccessFb = function (data) {
    console.error("here");
    if (data) {
        this.processItem(data);
    } else {
        this.wait();
    }
};

Consumer.prototype.run = function () {
    //this.port = helpers.getVendorPortById(this.currentItem);
    this.queue.pop().then(this.queueSuccessFb, this.failureCb);
};

exports.Consumer = Consumer;

我已经定义了一个测试,它基本上断言正确的工作流程正在发生,并且消费者最终处理队列中的所有任务(这是在真正的Redis代理之前工作的集成测试)

测试:

"use strict";

var consumer = require("./../../src/consumer"),
    queue = require("./../../src/queue"),
    Q = require("Q"),
    sinon = require("sinon"),
    assert = require("assert"),
    queueConf = require("./../../config/queue"),
    NUM_OF_ITEMS = 5,
    queueInstance,
    spy,
    consumerInstance;

describe("consumer", function () {
    beforeEach(function () {
        queueInstance = new queue.TaskQueue();
    });


    describe("waiting for tasks while the queue is empty", function () {
        describe("queue success call back", function () {
            before(function () {
                consumerInstance = new consumer.Consumer();
                spy = sinon.spy(consumerInstance, "queueSuccessFb");
            });

            it("should call the success callback once per the defined period", function (done) {
                consumerInstance.run();
                setTimeout(function () {
                    sinon.assert.calledOnce(spy);
                    done();
                }, queueConf.waitTime);
            });

            it("should call the success callback twice per the defined period + 1", function (done) {
                consumerInstance.run();
                setTimeout(function () {
                    sinon.assert.calledTwice(spy);
                    done();
                }, queueConf.waitTime * 2);
            });
        });

        describe("wait function", function () {
            before(function () {
                consumerInstance = new consumer.Consumer();
                spy = sinon.spy(consumerInstance, "wait");
            });
        });

    });

    describe("task handling", function () {
        beforeEach(function (done) {
            this.timeout(6000);
            var i, promises = [];
            queueInstance = new queue.TaskQueue();
            for (i = 1; i <= NUM_OF_ITEMS; i += 1) {
                promises.push(queueInstance.push(i));
            }
            Q.all(promises).then(function () {
                done();
            });

        });

        afterEach(function () {
            queueInstance.empty();
        });

        describe("sucess callback", function () {
            before(function () {
                consumerInstance = new consumer.Consumer();
                spy = sinon.spy(consumerInstance, "queueSuccessFb");
            });
            it("should run all of the available tasks one by one", function (done) {
                this.timeout(6000);
                consumerInstance.run();
                setTimeout(function () {
                    console.info(spy.callCount);
                    assert(spy.callCount === NUM_OF_ITEMS);
                    done();
                }, 2000);
            });
        });
    });
});

我的问题是呼叫计数总是等于1。 我一开始认为需要andCallThrough()方法调用,类似于它在Jasmine中的工作方式,但后来发现实际的函数被调用。

尝试使用sinon.useFakeTimers(),但根本不起作用(测试似乎没有等待,消费者类中的超时未触发);

预期行为:callCountNUM_OF_ITEMS(通过递归调用)。 实际行为:callCount始终为1。

1 个答案:

答案 0 :(得分:1)

Hi it's a little confusing to understand what your queue class is doing. Is it a singleton?

If it's not a singleton your consumer is initialised with a fresh empty queue on construction.

function Consumer() {
   this.queue =  new queue.TaskQueue();
   ...
}
...
    describe("success callback", function () {
        before(function () {
            consumerInstance = new consumer.Consumer();
            spy = sinon.spy(consumerInstance, "queueSuccessFb");
        });
        ....

This won't be the same as the queue you created in

describe("task handling", function () {
    beforeEach(function (done) {
       ...
       queueInstance = new queue.TaskQueue();
       ...
    });
    ...

And as the queues are not the same spy.callCount !== NUM_OF_ITEMS

Unless of course it's a singleton ie:

new queue.TaskQueue() === new queue.TaskQueue();

My suggestion is to enable the TaskQueue to be supplied to the Consumer constructor so you know that the consumer is operating over the expected queue

function Consumer(queue) {
   this.queue = queue;
   ...
}

describe("task handling", function () {
    beforeEach(function (done) {
       ...
       this.queueInstance = new queue.TaskQueue();
       ...
    });
    ...

    describe("success callback", function () {
        before(function () {
            consumerInstance = new consumer.Consumer(this.queueInstance);
            spy = sinon.spy(consumerInstance, "queueSuccessFb");
        });
        ....