10
votes

I use Karma (currently v0.10.10) and Jasmine for my unit tests, and Istanbul (via karma-coverage) for code coverage reports. I've noticed a strange behaviour of the code coverage reporter in a particular case.

The code I'm trying to test is roughly this:

/**
 * @param {HTMLInputElement} element
 */
var foo = function(element) {
    var callback = function() {
        // some code
    };

    element.addEventListener("input", callback);
};

In my test, I dispatch a custom input event on the tested element and the callback function executes. The test checks the effects of the callback, and the test passes. In fact, even when I put a hairy console.log("foo") in the callback, I can clearly see it being printed out. However, Istanbul's report erroneously indicates that the callback was not executed at all.

Modifying the tested code to use an anonymous function in the event listener's callback fixes the misbehaviour:

element.addEventListener("input", function() {
    callback();
});

However, I utterly despise "solutions" that modify the application's code to compensate for a code quality control tool's deficiency.

Is there a way I can make the code coverage get picked up correctly without wrapping the callback in an anonymous function?

2

2 Answers

0
votes

The callback is being passed into your method. Istanbul is completely unaware of where that callback came from, other than from your function definition. Istanbul knows that callback() came from the parameter callback, but doesn't know the insides of that callback (e.g. the function before it was passed in as a callback).

// edit example

var callback = function(args) {
    //do stuff
}

var foo = function (callback) {
    // do stuff
    callback(arguments);
}

Now create a test for foo's functionality, and a separate unit test for callback's functionality. A unit test should only test one thing. Each function should have it's own unit test. Test that foo does what it's supposed to (regardless of the callback) and that callback does what it's supposed to, (passing in your own mock data for the test). In general, named functions are always the way to go.

0
votes

I had this exact problem with callbacks in parts of my nodejs code not being marked as covered even though I had tests that definitely did cover them. I had this issue both with mocha/istanbul and with mocha/blanket.js.

I eventually noticed that many of my tests were not running with code coverage which lead me to the issue.

I solved it by adding the --recursive option to mocha as some tests were in sub-directories.