2

I'm testing a little node module with mocha and expect.js and I'm having a bit of a headache.

This is the test definition:

it('Should return an expansion array that contains all the objects that comes along within a single group', function (done) {

  visibility.expand(data, config.data, companies, groups, function (error, expansion) {

    expect(error).to.be(null);
    expect(expansion).to.be.an('array');
    expect(expansion).to.have.length(2);
    expect(expansion.toString()).to.eql([testCompaniesIds[2], testCompaniesIds[3]].toString());

    done();
  });
});
  • data is a wrapper for database access and functionality. I initialize it in the before method of my test suite.
  • config.data holds an object with config params (not relevant)
  • companies is an array of strings
  • groups is an array of strings

The problem that i'm facing is that when Mocha reaches this line

expect(expansion).to.have.length(2);

and the length is different from 2, instead of throwing an error like: Expected 2 but length was 1 it simply stops and throw an error because of the timeout.

I verified that the test executed until that line.

A bit more of information: the method that I'm testing receives an array of company names and an array of group names. Each group contains an array of company database id's.

The expected behaviour of the method is to return an array with corresponding company id's merged with the array of company id's belonging to the group object.

Edit 2 due to possible duplicate: I was wrong. It was indeed executing in the scope of a promise.

Edit due to possible duplicate: the expectation in not executing in the scope of a promise (when using promises and executing expect in either resolve or reject functions is the promise who catch the error).

Thanks in advance!

javierfdezg
  • 2,087
  • 1
  • 22
  • 31
  • Have you tried with : expect(expansion.length).to.be(2) ? – Clément Berthou Mar 20 '15 at 13:42
  • Yes, the effect is the same if the length is not 2 – javierfdezg Mar 20 '15 at 15:24
  • And does test pass if you test every line one by one (by commenting the others) ? Or if it doesn't, with which line does it fail ? – Clément Berthou Mar 20 '15 at 15:29
  • I just tried to remove all the expects except for that one and it fails with timeout. It's failing in the length line. – javierfdezg Mar 20 '15 at 17:52
  • Ok. So, the reason for a timeout, is that the done function is not called, or called after too long (I think 2 seconds is the default timeout setting on mocha). So either the callback is called after too long, the line is taking too much time/is stuck in an infinite loop or the done function is redefined at some point and the "correct" done function is not called. – Clément Berthou Mar 20 '15 at 18:57
  • And the three options seem unlikely. That's a pretty strange problem. – Clément Berthou Mar 20 '15 at 19:00
  • Yeah, a very strange problem. Next step will be debugging the expect.js module, but I'm not looking forward to doing it :( – javierfdezg Mar 20 '15 at 19:29
  • Have you tried testing the length without expect first, and then, testing the value of the boolean with expect, to see if the problem comes from expect testing the length ? – Clément Berthou Mar 20 '15 at 19:31
  • Just tried: expect(expansion.length === 4).to.be(false); (being length = 4) with the same result... timeout – javierfdezg Mar 20 '15 at 23:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/73456/discussion-between-oski555-and-clement-berthou). – javierfdezg Mar 21 '15 at 00:04
  • possible duplicate of [Mocha failed assertion causing timeout](http://stackoverflow.com/questions/18245611/mocha-failed-assertion-causing-timeout) – Louis Mar 21 '15 at 11:21
  • @Louis: edited the question to clarify why this question is not a duplicate – javierfdezg Mar 21 '15 at 13:17

3 Answers3

2

Wrapping all the test between a try-catch like this:

it('Should return an expansion array that contains all the objects that comes along within a single group', function (done) {

  visibility.expand(data, config.data, [testCompanies[0].name, testCompanies[1].name], [testGroups[0].name, testGroups[1].name], function (error, expansion) {
    try {
      expect(error).to.be(null);
      expect(expansion).to.be.an('array');
      expect(expansion).to.have.length(2);

      expect(checkIdIsContainedInArray(testCompaniesIds[0], expansion)).to.be(true);
      expect(checkIdIsContainedInArray(testCompaniesIds[1], expansion)).to.be(true);
      expect(checkIdIsContainedInArray(testCompaniesIds[2], expansion)).to.be(true);
      expect(checkIdIsContainedInArray(testCompaniesIds[3], expansion)).to.be(true);

      done();
    } catch (e) {
      done(e);
    }
  });
});

This test throws an error due to array length (is 4 and should be 2):

  Error: expected [ '464142414441464142414441',
  '464142414441464142414442',
  '464142414441464142414443',
  '464142414441464142414444' ] to have a length of 2 but got 4

Instead of:

Error: timeout of 2000ms exceeded

which can mean anything.

Debugging expect.js I saw that it was throwing an error but Mocha didn't manage to capture it.

Although this solution might not be as elegant as desired, at least it gives the desired feedback instead of a timeout.

javierfdezg
  • 2,087
  • 1
  • 22
  • 31
2

You are executing the expectations in a callback function.

This means that the code is being executed in the visibility.expand method.

I'm pretty sure that the behaviour that you are seeing is because of the use of promises... are you using promises in the visibility.expand method?

aImazio
  • 36
  • 4
1

I believe this is actually working as expected.

The Mocha tests are only able to end the async call when the done() is actually called. Since an error is thrown outside of the scope of the mocha execution context, it never reaches that block of code and the execution basically runs out of time.

I can't seem to find any official documentation stating this, but here's a few (somewhat related hopefully?) references -

https://github.com/pouchdb/pouchdb/issues/1339#issuecomment-34739526

http://chaijs.com/guide/styles/#expect //search around for error handling on callbacks

Is there a way to get Chai working with asynchronous Mocha tests?

Mocha failed assertion causing timeout

Edit - Actually, I'm seeing that I had it backwards, it should be catching the exception for sure. I would relook at the original code again and see if you have everything correct. Maybe update expect.js and see if it has some issues on your current version (or file a ticket). I use chai with the expect library and am able to get it to error fine. http://chaijs.com/

Code example with error being thrown correctly -

var chai = require('chai')
    , expect = chai.expect;

describe('something', function () {

    var j = [1];

    it('does something.. ', function (done) {
        setInterval(function () {
            console.log('its done');
            expect(j).to.be.length(2);
            done();
        }, 1000);
    });

});  

//Uncaught AssertionError: expected [ 1 ] to have a length of 2 but got 1
Community
  • 1
  • 1
Justin Maat
  • 1,965
  • 3
  • 23
  • 33
  • Hi Justin. This make sense, however I've got other tests which I'm using with expect.js that throw an error and Mocha is able to capture it. I'm a bit confused with all this. Thanks for the references by the way. – javierfdezg Mar 21 '15 at 01:02
  • Uhm, interesting. I think is not an expect.js issue because it is detecting the error and throwing it up. I'm going to try to dig a bit more into mocha and sure I'll try out chai's expect method. – javierfdezg Mar 21 '15 at 01:18