Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching out-of-scope errors with Mocha and Chai

I am modifying a node.js library to support true asynchronous operations.

I am having troubles with Mocha and Chai to make this (a similar) test pass.

it('should throw an error', function() {
    expect(function() {
        process.nextTick(function() {
            throw new Error('This is my error');
        });
    }).to.throw(Error);
});

The problem is - because of the nextTick - that the Error is thrown out of scope of it and besides the test failing, Mocha also outputs the below.

Uncaught Error: This is my error

What is the proper way to structure this test in order to make it succeed?

like image 463
Trenskow Avatar asked Nov 03 '14 17:11

Trenskow


1 Answers

Hmm... in a full-fledged application what I'd do is probably use something like Sinon to check that the method that should throw an error has been called and is throwing.

In code where you cannot do this, then the following method would trap the exception:

var expect = require("chai").expect;
var domain = require("domain");

it('should throw an error', function(done) {
    var d = domain.create();
    d.on('error', function (err) {
        // Exit the current domain.
        d.exit();
        // We must execute this code at the next tick.
        process.nextTick(function () {
            console.log(err); // Just to show something on the console.
            expect(err instanceof Error).to.be.true;
            done();
        });
    });


    d.run(function () {
        process.nextTick(function() {
            throw new Error('This is my error');
        });
    });
});

This code creates a "domain" stored in d. A domain will emit error events on uncaught exceptions that happen in it so we run the test inside the domain (d.run(...)) we've created and wait for an exception to happen (d.on('error', ...). We check that it is an Error object. (In a real test, I'd also check the error message.) When we are finished we call done() to tell Mocha that the asynchronous test is over.

The handler for error events calls d.exit(). This is to make it so that Mocha can catch the error normally if the assertion (expect(err instanceof Error)...) turns out to fail. If we do not exit the domain, then the domain will trap the error. Also, the check itself must be performed on the next tick to be outside the d domain.

Is Using domain A Problem?

NO!

The documentation for domain comes with some warnings about shutting down operations once an uncaught exception is caught when running an ongoing process, like a server. Then the thing to do is to clean what can be cleaned and exit as soon as possible. However, using domain in a test does not differ from what Mocha is already doing. The way Mocha catches unhandled exceptions in asynchronous code is by using process.on('uncaughtException'. Upon catching an unhandled exception Mocha marks the current test as failed and continues. Yet the documentation regarding uncaughtException says "Don't use it, use domains instead. If you do use it, restart your application after every unhandled exception!"

Ergo, anyone who has a problem with using domain should not be using Mocha in the first place.

like image 191
Louis Avatar answered Oct 04 '22 03:10

Louis