简体   繁体   中英

How to mock event handler method using sinon.js?

I am newbie to Node.js and I am writing DAO layer for HBase which will wrap thrift and provide clear interface to other layers. I am trying to write unit tests for it using sinon.js and mocha but not sure how to ensure mock one event of Thrift connection class and its event handler.

My DAO code is as follows:

var thrift = require('thrift');
var libDirRelativePath = "../../../lib";
var hbaseThriftDirPath = libDirRelativePath + "/hbase-gen-nodejs";
var hbase = require(hbaseThriftDirPath + '/THBaseService');
var hbaseTypes = require(hbaseThriftDirPath + '/hbase_types');

var thritfPrimaryServerAddress = 'nn2';
var thritfBackupServerAddress = 'backup-nn2';
var thriftServerPort = 9090;

exports.putRows = function(tableName, putObjectArray, callback) {
    var primaryClusterConnection = thrift.createConnection(thritfPrimaryServerAddress, thriftServerPort, {
      transport: thrift.TBufferedTransport,
      protocol : thrift.TBinaryProtocol
    });

    console.log('DEBUG : connection object created.');

    var client = thrift.createClient(hbase, primaryClusterConnection);
    console.log('DEBUG : client object created.');

    primaryClusterConnection.on('connect', onConnectOfPutRows);

    primaryClusterConnection.on('connect', function() {
        console.log('Connected to HBase thrift server at ' + thritfPrimaryServerAddress + ":" + thriftServerPort);
        client.putMultiple(tableName, putObjectArray, callback);
        connection.close();
    });

    primaryClusterConnection.on('error', function() {
        console.log('Error occurred in HBase thirft server connection.');
    });
}

For above code I Just want to create stubs primaryClusterConnection and client objects which I have managed but problem is that stub of primaryClusterConnection doesn't have any idea about connect event and its handler so console.log('Connected to HBase thrift server at '... line never gets executed. I want to test that part of the code as well. Can anyone please help me in writing proper stubs/mocks for this problem?

My test code is as follows:

var hbaseDao = require('../../../src/dao/hbase/HBaseDao.js');
var libDirRelativePath = "../../../lib";
var hbaseThriftDirPath = libDirRelativePath + "/hbase-gen-nodejs";
var hbase = require(hbaseThriftDirPath + '/THBaseService');

var chai = require('chai');
var should = chai.should();
var expect = chai.expect;
var sinon = require('sinon');

describe("HBaseDao", function() {
    describe(".putRows()", function() {

        it("Should execute callback after inserting objects in HBase.", function(done) {
            var commonStub = sinon.stub();
            var connection = {
                close : function() {
                    console.log('connection closed.');
                }
            };
            commonStub.withArgs('nn2', 9090).returns(connection);
            var client = {};
            commonStub.withArgs(hbase, connection).returns(client);
            var tableName = 'DUMMY_READINGS_TABLE';
            var callBackMethod = function() {
                console.log('dummy callback function.');
            };
            commonStub.withArgs(tableName, [], callBackMethod).returns(0);

            hbaseDao.putRows(tableName, [], callBackMethod);
            expect(hbaseDaoSpy.callCount).to.equal(1);
            done();
        });

Let's start by simplifying the problem a bit.

it.only("Should execute callback after inserting objects in HBase.", function(done) {

    var events = require('events');
    var hbaseDao = new events.EventEmitter();
        hbaseDao.putRows = function() {
            console.log('putting rows');
            this.emit('notify');
        };
        hbaseDao.on('notify', function(){
            console.log('notify event fired');
            done(); //here's where you call the callback to assert that the event has fired
        });

    sinon.spy(hbaseDao, 'putRows');

    var commonStub = sinon.stub();
    var tableName = 'DUMMY_READINGS_TABLE';
    var client = {};
    var connection = {
        close : function() {
            console.log('connection closed.');
        }
    };
    var callBackMethod = function() {
        console.log('dummy callback function.');
    };

    commonStub.withArgs('nn2', 9090).returns(connection);
    commonStub.withArgs({}, connection).returns(client);
    commonStub.withArgs(tableName, [], callBackMethod).returns(0);

    hbaseDao.putRows(tableName, [], callBackMethod);

    //assertions
    assert(hbaseDao.putRows.calledOnce);

});

The above test will just work, because it creates a new "hbaseDao" from a simple event emitter and has the method and the notify event ready to go.

Because we're doing an async test, we need to have the done callback in the spec. Notice that this will only fire "done" when the event has occurred. Hence, the test will not pass unless the event fires. Also notice that we're spying specifically on the the hbaseDao 'putRows' and we're asserting that the its called once, another way to ensure that the test is working. Now consider this example and apply it to your original question.

I think you almost got it, but you need to put your done callback in the callback stub as so:

var callBackMethod = function() {
    console.log('dummy callback function.');
    done();
 };

That way, when your primaryClusterConnection.on('connect') event is fired, the supplied callback will execute the done and complete the test.

That being said, you should leave your primaryClusterConnection intact and let the implementation details of hbaseDao not be considered in your test.

You mentioned that:

primaryClusterConnection doesn't have any idea about connect

But that can't be right, because you're creating a new connection in the test and there's nothing in your implementation that tells me you have changed the event handler for the connection.

So I think in the end, you're missing the point of the test, which is simply should execute callback... and you're stubbing out stuff that you don't even need to.

Try something like this:

//use it.only to make sure there's no other tests running
it.only("Should execute callback after inserting objects in HBase.", function(done) {

    //get the class
    var hbaseDao = require('../../../src/dao/hbase/HBaseDao.js');

    //spy on the method
    sinon.spy(hbaseDao, 'putRows');

    //create a table name
    var tableName = 'DUMMY_READINGS_TABLE';

    //create callback method with done.
    var callBackMethod = function() {
        console.log('dummy callback function.');
        done();
    };

    //run the function under test
    hbaseDao.putRows(tableName, [], callBackMethod);

    //assert called once
    assert(hbaseDao.putRows.calledOnce);

});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM