简体   繁体   中英

Node.js how should I unit test a function calling other functions

I want to unit test a module I've built. To give an impression of what it looks like pretty much.. MyModule:

function MyModule(...) {
    var self = this;
    MyModule.init.call(self, ...);
}

util.inherits(MyModule, MySuperModule);

MyModule.init = function(...) {
...
};

MyModule.prototype.logic = function(..., done) {
   calls fnA, fnB, fnC, fnD conditionally
   done(...)
};

MyModule.prototype.fnA = function(...) {
...
};

MyModule.prototype.fnB = function(...) {
...
};

MyModule.prototype.fnC = function(...) {
...
};

MyModule.prototype.fnD = function(...) {
...
};

MySuperModule:

function MySuperModule(...) {
    ...
    }

    MySuperModule.prototype,fn = function(..., done) {
        var self = this;
        ...
        self.logic(..., function done(...) {
            ...
            done(...)
        });
    }

Now MyModule.logic() is never called explicitly by a user, it is only invoked MySuperModule.fn(). Same goes for all other MyModule functions which are called conditionally based on the given parameters being passed through the delegating chain.

My questions are as follow:

  • Do I need to test all MyModule functions separately or just test MySuperModule.fn() with different parameters covering all possible scenarios
  • I know I need to test function in isolation (which if I do my previous question is wrong to ask because than I won't really have tested MyModule functions at all), how would I do that with the MySuperModule.fn(), because its done() callback is being called with arguments dependent on what the MyModule.logic() done() callback was called with, which is again, dependent on the arguments supplied to MySuperModule.fn() arguments.

In my view you should certainly be testing the individual functions, regardless of whether or not they're called directly by a user.

The purpose of unit testing is to try to ensure that the individual units of your test do what they're expected to do. If you're (relatively) sure that your individual functions/units behave as expected, you can have more confidence that they'll work nicely with each other.

It's hard to really glean from your code snippets the nature of your module, so suggesting how to implement your tests is difficult. However, it seems like what you're asking is how to verify whether your done/callback function is called and with which arguments. For that I would recommend using a stub. I usually use sinon but I'm sure other similar tools are available.

    var sinon = require( "sinon" );
    var should = require( "chai" ).should();
    var yourModule = require( "your-module" );

    var doneStub = sinon.stub();
    yourModule.yourFunction( ..., doneStub );
    doneStub.should.have.been.called;
    var args = doneStub.getCall( 0 ).args;
    args[ 0 ].should.be.eql( ... );
    // etc etc 

You should also consider using a test runner, I like mocha !

It really depends how you're injecting MyModule on MySuperModule. But first of all I would point out that in unit tests you have to test MyModule separately and MySuperModule with a Mocked version from MyModule and all other dependencies. This is because you don't want to test MyModule twice, no need for that.

So to create stubs there is a library called Sinon.JS which works really fine.

So if for any reason you just want to make a spy to MyModule, which means you are just attaching a listener to MyModule (it is applied to MyModule methods) which counts and tells if a given method is ever called and how.

var MyModuleMethodASpy = sinon.spy(MyModulem 'MethodA');
MySuperModule.someMethod();
assert(MyModuleMethodASpy.called)

So this code creates a spy, triggers some method on MySuperModule and checks if MyModule.MethodA() is ever called.

You can create stubs as well if you want to control what dependencies return on specific methods eg :

var MyModuleStub = sinon.stub(MyModule, 'MethodA').returns([...somedata]);

You should do progressive testing. You should test each and every function. Here how can you proceed.

  1. Write test case for parent function. Mock the inner function where it is calling. You can use sinon library for mocking.
  2. For second question, you can use sinon mock's yield functionality to mock any callback function and you can specify also which output you want from that callback. In this way you can test your function for multiple custom output with different scenario.

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