Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Express errors with Mocha and Supertest

I have to test for server errors (Express) in acceptance tests that can't (or shouldn't) be sent with response, for example

Error: Can't set headers after they are sent.

Catching an error with error handler and responding with 5XX code would provide valuable feedback here, but the problem is that the headers have been sent already.

This kind of bugs may be noncritical and hard to spot, and usually they are figured out from the logs.

The spec is

it('should send 200', function (done) {
    request(app).get('/').expect(200, done);
});

And tested app is

app.get('/', function (req, res, next) {
    res.sendStatus(200);
    next();
});

app.use(function (req, res) {
    res.sendStatus(200);
});

What is the most appropriate way to communicate between Express app instance and request testing library (i.e. Supertest) in similar cases?

The question is not restricted to Supertest. If there are packages that can solve the problem that Supertest can't, they may be considered as well.

like image 419
Estus Flask Avatar asked Feb 23 '16 20:02

Estus Flask


People also ask

What is SuperTest in Mocha?

SuperTest enables you to make any HTTP requests with ease. The Mocha testing framework organizes and runs your tests in the way your team prefers, whether its TDD or BDD style.

Does Mocha run tests in parallel?

Mocha does not run individual tests in parallel. That means if you hand Mocha a single, lonely test file, it will spawn a single worker process, and that worker process will run the file. If you only have one test file, you'll be penalized for using parallel mode. Don't do that.

Is Mocha used for unit testing?

For your NodeJS applications, Mocha and Chai can be used together for Unit Testing.


2 Answers

Try to just set the status code without send it and avoid send it twice throwing the error use res.status().

As the express documentation say it

Sets the HTTP status for the response. It is a chainable alias of Node’s response.statusCode.

IMHO if you want to detect it in a end-to-end (E2E) testing tool like supertest (or Selenium) you have to handle the express error and send a correct output (500 status error, some message...) to permit detect it.

Or use instead a unit test, to test that the controller function doesn't throw any error using chai or a native assertion.

like image 70
Dario Avatar answered Oct 16 '22 23:10

Dario


I answered a similar question here. The "can't set headers after they have been set" error should be raised by express as an unhandled exception. So, you should be able to get access to it via the unhandledException event that is raised by the process.

However, this is much more tricky because of the timing. Your test case's expect function and done function will be queued for processing on the event loop on the tick right after the first res.statusCode call. Unfortunately, the next call for res.statusCode can happen at an indeterminate amount of time afterwards. For example, what if the second route handler called a really slow webservice or db and then called res.statusCode.

With that in mind, your options are pretty hard. The brute force way is to wait in your test code for a determinate amount of time and then check. It's effective but slow and non-deterministic, which will cause your test to be flaky.

Another option is to check any instrumentation code that you might have in express. If you have code in express that keeps metrics of the number of in process calls for the various route handlers you could expose these metrics to your test code. Then one of your conditions for finishing your test is that all metrics for in process route calls are 0. The second option would allow you to write deterministic tests and be much faster because you could poll the metrics.

The final option would be to handle this test case through unit tests. This is probably the best solution because it would be deterministic and wouldn't require any sort of polling. However, the downside is that you need to know that both of your functions are called in order which leads you down a path of trying to recreate in your test code the logic that express uses to call route handlers.

like image 26
Zambonilli Avatar answered Oct 17 '22 01:10

Zambonilli