Jacob Clark
Jacob Clark

Reputation: 3447

Mocha & Sinon method spies

I am setting up tests for my application, and I wish to check a method was called x times using Sinon, my testing framework is Mocha.

How can I achieve this, below is the code I wish to test, I'm looking to ensure recursiveRequest is called x times by createClients.

Nodezilla.prototype.createClients = function(batched, i){
    var self = this;

    if(batched)
        batchRequests();

    if(batched == false && i < this.virtualUsers){
        // set timeout to help prevent out of memory errors
        setTimeout( function() {
            self.createClients(false, i+1);
        }, 0 );  
    }else{
        this.recursiveRequest();
    }
};

Nodezilla.prototype.recursiveRequest = function(){
    var self    =   this;
    self.hrtime =   process.hrtime();

    if(!this.halt){
        self.reqMade++;

        this.http.get(this.options, function(resp){
            resp.on('data', function(){})
                .on("end", function(){
                    self.onSuccess();
                });
        }).on("error", function(e){
            self.onError();
        });
    }
};

Attempted test but no avail as callCount returns 0.

var assert      = require('assert'),
    sinon       = require('sinon'),
    nz          = require('../Nodezilla'),
    nt          = new nz("localhost", 10);

describe('Nodezilla', function(){
  describe('createClients', function(){
    before(function() {
        sinon.spy(nt, "recursiveRequest");
    });

    it('should call recursiveRequest() 10 times', function(){
        nt.createClients(false, 0);

        assert(nt.recursiveRequest.callCount);

    });
  });
});

Upvotes: 3

Views: 5461

Answers (1)

Selfish
Selfish

Reputation: 6200

createClients seems to be an async request, without a callback/promise. This means your test is evaluated immediately, and does not wait for results. I'd suggest to re-write the function with a callback or promise so you are able to act on the event of processing completed, and then this should work:

var assert = require('assert'),
    sinon  = require('sinon'),
    nz     = require('../Nodezilla'),
    nt     = new nz("localhost", 1);

describe('Nodezilla', function () {
    describe('createClients', function () {
        it('should call recursiveRequest() 10 times', function (itCallback) {
            // Moved into the test:                
            sinon.spy(nt, "recursiveRequest");
            nt.createClients(false, 0, function(){
                // Needs to wait for action to actually be called:
                assert(nt.recursiveRequest.callCount == 10);
                // Now that the test is actually finished, end it:
                itCallback();
            });
        });
    });
});

Skipping the before statement, as this might interfere with scopes, sinon.spy being synchronous can be called inside the test.

Also note I have introduced a callback in this statement: it('should call recursiveRequest() 10 times', function (callback) { to hold the test from finishing before the inner callback is called.

Edit: As for adding the callbacks, I'm not sure what does batchRequests() does, but go like that:

Nodezilla.prototype.createClients = function (batched, i, cb) {
    var self = this;

    if (batched)
        batchRequests();

    if (batched == false && i < this.virtualUsers) {
        // set timeout to help prevent out of memory errors
        setTimeout(function () {
            self.createClients(false, i + 1, cb);
        }, 0);
    } else {
        this.recursiveRequest(cb);
    }
};

And then:

Nodezilla.prototype.recursiveRequest = function (cb) {
    var self = this;
    self.hrtime = process.hrtime();

    if (!this.halt) {
        self.reqMade++;

        this.http.get(this.options, function (resp) {
            resp.on('data', function () {
            })
                .on("end", function () {
                    self.onSuccess();
                    cb();
                });
        }).on("error", function (e) {
            self.onError();
            cb();
        });
    } else {
        // I assume you are doing nothing in this case, so just call the callback:
        cb();
    }
};

Also note you can use the callback for error handling.

Upvotes: 3

Related Questions