0

OK, so I have all of these tests that depend on node-mocks-http in mocha to test my express.js controllers and they are all still working just fine.

The problem is that any new tests I create refuse to emit the 'end' event and finish. It got tot the point where I decided to write a very very simple controller function and test in a completely fresh project and I am still not getting the 'end' event to fire.

This is my controller (index.js):

exports.testFunc = function(req, res) {
  res.status(200).json({
    status: 'success',
    status_message: 'OK',
    data: {
      firstRun: true
    }
 });
}

And this is my test (test.js):

let httpMocks = require('node-mocks-http');

let request = httpMocks.createRequest({
    method: 'GET',
    url: '/test',
});

let response = httpMocks.createResponse({
    eventEmitter: require('events').EventEmitter
});

const loadingController = require('./index');

describe('loadingController:', () => {

    describe('first_run_status: ', () => {
        it('should really respond here', (done) => {
            loadingController.testFunc(request, response);
            response.on('end', () => {
                done();
            });
        });
    });
});

This is the exact code I have running with

./node_modules/mocha/bin/_mocha ./test.js --timeout 10000 --exit

This absolutely refuses to emit the 'end' event and end the test...

This is really driving me crazy, can anyone see what is wrong here? or what could possibly make this not work?

FYI when I log the response object to the console before running the testFunc I can see finished: false and when I log it after I can see

EventEmitter {
  _headers: { 'content-type': 'application/json' },
  cookies: {},
  finished: true,
  headersSent: true,
  statusCode: 200,
  ....etc.

that finished: true so why would the 'end' event not be emited?

2 Answers2

2

OK, pretty sure I figured this out...took, like I dunno 8 hours of wrangling and getting down to inspecting the response objects event emitter at a low level at every stage of this simple experiment. You have to hook up the event emitter like so

response.on('end', () => {

});

test.test(request, response);

BEFORE you call your route function...kind of a no duh, but hey, maybe this will save someone the pain.

You MUST place the response.on('end', () => ... before you call your route/function...

jeeze I wish their docs would have clarified this requirement, Obviously they show it in their example, but they don't clarify the necessity of it.

but I guess people that understand js at an expert level already know this kind of stuff about EventEmmiters.

...seems obvious in retrospect, but because my code was WORKING (still is working) with it hooked up backwards, it was less then obvious. The only reason this had been working for me everywhere in my app even though I did this completely backwards EVERYWHERE with calling the test route/controller before hooking up the event emitter is because by some dumb luck all of my routes took just milliseconds long enough that the event was not emitted until after the event handler had a chance to hook up...so when I started converting everything to being synchronous and much more unit testy it suddenly stopped working.

hopefully some good will come of this in the future.

1

Thank you San Francisco

The code will work by calling the end event before calling the route

So the following code will work

describe('first_run_status: ', () => {
       it('should really respond here', (done) => {
        // put your asserts before calling the route
        response.on('end', () => {
            expect(1).toBe(1)
        })
        loadingController.testFunc(request, response)         
       })
   })
shobekhan
  • 171
  • 1
  • 2
  • 15