Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocha test suite errorring out when attempting to connect to API

I'm running my test suite using mocha, via gulp-jsx-coverage and gulp-mocha. All my tests run and pass/fail as expected. However, some of my modules being tested make HTTP requests to my API via the superagent library.

When in development, I'm also running my API at localhost:3000 alongside my client-side app, and so that is the URL my client-side tests are attempting to access. When testing, though, the API is usually not running. This results in the following error any time a request gets through:

Error in plugin 'gulp-mocha'
Message:
    connect ECONNREFUSED
Details:
    code: ECONNREFUSED
    errno: ECONNREFUSED
    syscall: connect
    domainEmitter: [object Object]
    domain: [object Object]
    domainThrown: false
Stack:
Error: connect ECONNREFUSED
    at exports._errnoException (util.js:746:11)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:983:19)

I've tried stubbing all of the methods on the superagent (aliased as request) library in a global helper, like so:

function httpStub() {
  return {
    withCredentials: () => {
      return { end: () => {} };
    }
  };
};

beforeEach(function() {
  global.sandbox = sinon.sandbox.create();

  global.getStub = global.sandbox.stub(request, 'get', httpStub);
  global.putStub = global.sandbox.stub(request, 'put', httpStub);
  global.patchStub = global.sandbox.stub(request, 'patch', httpStub);
  global.postStub = global.sandbox.stub(request, 'post', httpStub);
  global.delStub = global.sandbox.stub(request, 'del', httpStub);
});

afterEach(function() {
  global.sandbox.restore();
});

But for some reason, when some tests are encountered the methods aren't stubbed and so I reach the ECONNREFUSED error. I've triple checked, and no where am I restoring the sandbox or any stubs.

Is there either a way to fix the problem I'm running into, or a cleaner solution for this overall?

like image 567
Jakemmarsh Avatar asked Jan 03 '16 02:01

Jakemmarsh


2 Answers

The problem might might be caused by not doing asynchronous stuff properly in your test. Imagine following example:

it('is BAD asynchronous test', () => {
  do_something()
  do_something_else()
  return do_something_async(/* callback */ () => {
    problematic_call()
  })
})

When Mocha finds such a test it executes (synchronously) do_something, do_something_else and do_something_async. In that moment, from Mochas perspective the test is over, Mocha executes afterEach() for it (what is bad, the problematic_call is still yet to be called!) and (what is even worse), it start running the next test!

Now, obviously, running tests (and beforeEach and afterEach) in a parallel manner can lead to really strange and unpredictable results, so it's no surprise something got wrong (probably afterEach was called in the middle of some test which lead to unstubbing the environment)

What to do with it:

Always signal to Mocha, when your test ends. This can be done either by returning a Promise object, or, by calling done callback:

it('is BAD asynchronous test', (done) => {
  do_something()
  do_something_else()
  return do_something_async(/* callback */ () => {
    problematic_call()
    done()
  })
})

https://mochajs.org/

This way Mocha 'knows' when your test ends and only then it runs the next test.

like image 102
Tomas Kulich Avatar answered Nov 14 '22 08:11

Tomas Kulich


request has already been required by your application, so it doesn't matter whether or not your stub it in your tests.

You need to use something like rewire and replace the request required by your application with your stubbed version.

Something like this should help

var request = require('superagent'),
    rewire = require('rewire'),
    sinon = require('sinon'),
    application = rewire('your-app'),
    rewiredRequest;

function httpStub() {
    return {
        withCredentials: () => {
            return { end: () => {} };
        }
    };
};

beforeEach(function() {
    var requestStub = {
        get: httpStub,
        put: httpStub,
        patch: httpStub,
        post: httpStub,
        del: httpStub
    };

    // rewiredRequest is a function that will revert the rewiring
    rewiredRequest = application.__set__({
        request: requestStub
    });
});

afterEach(function() {
  rewiredRequest();
});
like image 37
elssar Avatar answered Nov 14 '22 06:11

elssar