mbochynski
mbochynski

Reputation: 706

Assertion in event brake down Mocha when run programmatically

I have problem with Mocha. If i run this programmaticaly from Jake Mocha brakes down and don't show nothing more than some errors stuff like:

AssertionError: There is a code 200 in response
    at Socket.<anonymous> (/home/X/Y/Z/test/test_Server.js:70:4)
    at Socket.EventEmitter.emit (events.js:93:17)
    at TCP.onread (net.js:418:51)

Runned from command line gives more expected results. That is:

19 passing (30ms)
  7 failing

  1) RTDB accepts connection with package and response with code 200 if correct package was send:
     Uncaught AssertionError: There is a code 200 in response
      at Socket.<anonymous> (/X/Y/Z/test/test_Server.js:70:4)
      at Socket.EventEmitter.emit (events.js:93:17)
      at TCP.onread (net.js:418:51)

  2) XYZ should be able to store GHJ for IJS:
     Error: expected f...
...

The problem is following code:

test('accepts connection with package and response with code 400 ' +
        'if wrong package was send', function (done) {
    console.log('client connecting to server');
    var message = '';
    var client = net.connect(8122, 'localhost', function () {
        client.write('Hello');
        client.end();
    } );
    client.setEncoding('utf8');

    client.on('data', function (data) {
        message += data;
    } );

    client.on('end', function (data) {
        assert(message.indexOf('400') !== -1, 'There is a code 400 in response');
        done();
    });

    client.on('error', function(e) {
        throw new Error('Client error: ' + e);
    });
});

If I do

assert(message.indexOf('400') !== -1, 'There is a code 400 in response');

just after

var message = '';

Mocha fails correctly (I mean displaying errors etc.), So this is fault of asynch assertion done on event. How can I correct that? Thats real problem Because this test is first, and I get no clue where to look for source of problem (If there is any). Should I somehow catch this assertion error and pass it to Mocha?

EDIT: Answer to comment how is Jake running Mocha - just like that:

var Mocha = require('mocha');
...
task("test", [], function() {
    // First, you need to instantiate a Mocha instance.
    var mocha = new Mocha({
        ui: 'tdd',
        reporter: 'dot'
    });

    // Then, you need to use the method "addFile" on the mocha
    // object for each file.
    var dir = 'test';

    fs.readdirSync(dir).filter(function(file){
        // Only keep the .js files
        return file.substr(-3) === '.js';

    }).forEach(function(file){
        // Use the method "addFile" to add the file to mocha
        mocha.addFile(
            path.join(dir, file)
        );
    });

    // Now, you can run the tests.
    mocha.run(function(failures){
        if(failures){
            fail("Mocha test failed");
        } else {
            complete();
        }
    });
}, {async: true});

Upvotes: 0

Views: 845

Answers (3)

mbochynski
mbochynski

Reputation: 706

Based on this answer: https://stackoverflow.com/a/9132271/2024650

In few words: I remove listener on uncaughtException in Jake. This allow Mocha to handle this uncaughtExceptions. At the end I add back this listener.

This solves my answer for now:

task("test", [], function() {

    var originalExeption = process.listeners('uncaughtException').pop();
    //!!!in node 0.10.X you should also check if process.removeListener isn't necessary!!!

    console.log(originalExeption);

    // First, you need to instantiate a Mocha instance.
    var mocha = new Mocha({
        ui: 'tdd',
        reporter: 'dot'
    });

    // Then, you need to use the method "addFile" on the mocha
    // object for each file.
    var dir = 'test';

    fs.readdirSync(dir).filter(function(file){
        // Only keep the .js files
        return file.substr(-3) === '.js';

    }).forEach(function(file){
        // Use the method "addFile" to add the file to mocha
        mocha.addFile(
            path.join(dir, file)
        );
    });

    // Now, you can run the tests.
    mocha.run(function(failures){
        if(failures){
            fail("Mocha test failed");
        } else {
            complete();
        }
        process.listeners('uncaughtException').push(originalExeption);


    });
}, {async: true});

Upvotes: 1

Louis
Louis

Reputation: 151401

I'm assuming since you say "programmatically" that your Jakefile issues require("mocha") and then creates a Mocha object on which it calls the run method.

If this is the case, then the reason it does not work is because Jake and Mocha are working at cross purposes. When Mocha executes a test, it traps unhandled exceptions. Schematically, (omitting details that are not important) it is something like:

try {
    test.run();
}
catch (ex) {
    recordFailure();
}

It is at the call to test.run that the test is executed. For tests that are purely synchronous, no problem. When a test is asynchronous, the asynchronous callback which is part of the test cannot execute inside the try... catch block Mocha establishes. The test will launch the asynchronous operation and return immediately. At some point in the future, the asynchronous operation calls the callback. When this happens, Mocha is not able to catch the exception in the asynchronous operation with a try... catch block. How does it catch such exceptions then? It listens to uncaughtException events.

Now, the problem when Mocha is run in the same execution context as Jake is that Jake also wants to trap uncaught exceptions. Jake sometimes has to launch asynchronous operations and wants to trap cases where these operations fail, so it listens to uncaughtException too. It installs its listener first. So when an asynchronous Mocha test fails with a exception, Jake's listener's is called, which cause Jake to immediately stop execution. Mocha never gets a chance to act.

I don't see a clear way to make both Jake and Mocha cooperate when run in the same execution context. There might be a way to fiddle with the handlers but I doubt that there is a robust way to make it work. (By "robust" I mean a way which will ensure that every single error is trapped and attributed to the correct source.) The vm module might help segregate their contexts while keeping them in the same OS process.

Upvotes: 2

Gntem
Gntem

Reputation: 7165

it seems that you are testing a HTTP server by connecting with TCP to it, (correct if i'm wrong) if thats the case here, you should just drop your current test, and use appropriate module to test an HTTP server or REST API there are plenty modules like Superagent .


You should try call client.end() after you have received your first data event, this way it will call assert after first header is received.

if you want to continuously send and test requests, have all you assert in the data event and call correct assert each time it receives the header you want to test, just remember that you must call done() when its supposed to finish, and that it can't be delayed for a long period of time, the request must be one after another.

Other than that you can use async module if you want to test chained requests ( one request depends on another and another and so on..), in some case its useful to raise the mocha timeout to more than 10000 (10secs) to give the async part some time to complete.

Upvotes: 0

Related Questions