Don't rip your hair out when Vows tells you "Errored callback not fired" - try this one weird trick instead

Topics: 

When your vows.js based tests for a Node.js application says "Errored » callback not fired" -- well, it can be very confusing. In my case the code clearly handled all paths ensuring the Vows callback would be called. No matter how many tweaks I performed to try and catch some possible error in test or code, I couldn't figure out what caused this problem. But after some yahoogling, the answer was not only difficult to find, but surprisingly simple.

Click here for more Node.js programming advice

To review, an asynchronous test in Vows.js is supposed to follow this pattern:

{ topic: function () {
    fs.stat('~/FILE', this.callback);
  },
  'can be accessed': function (err, stat) {
    assert.isNull   (err);        // We have no error
    assert.isObject (stat);       // We have a stat object
  },
  'is not empty': function (err, stat) {
    assert.isNotZero (stat.size); // The file size is > 0
  }
}

The code "this.callback" is a little piece of magic that Vows uses to capture the data provided by the asynchronous callback. It's supposed to be used in the typical pattern for callbacks: "function(err, other, arguments)" and Vows will dutifully capture everything and do appropriate stuff with it.

What if your code needs to make a couple layers of asynchronous calls? A little trick to consider is that "this" won't have the same value for subsequent layers of asynchronous calls as it has at the first layer. Hence, you should do something like

{ topic: function () {
    var that = this;
    fs.stat('~/FILE', function(err, stats) {
        if (err) that.callback(err);
        else {
            doSomethingElse(arg, arg2, function(err2, other, data) {
                if (err2) that.callback(err2);
                else that.callback(null, other, data);
            });
        }
    });
  },
...
}

The point is to ensure you call "callback" function provided by Vows.

In my test, I'd done that, but still it told me the callback wasn't being triggered. It seems from what I found while searching that other people have also beat their heads against this particular wall.

What I eventually discovered was deceptively simple. The problem is that Vows does something to the normal logging output circumventing its display, I suppose so the pretty report can be printed on the console? What's necessary is to add this code to your test case.

process.on('uncaughtException', function(err) {
console.log('Caught exception: ' + err.stack);
});

The problem I had was that - indeed - the callback wasn't being triggered, and that's because of an error buried inside the code that was being tested. That error wasn't being caught and Vows also ensured the information didn't get printed while still causing the test case to fail mysteriously.

The error in my code isn't important to this story. What IS important is that as soon as the test was run with this code, the error was immediately obvious and immediately fixable.