Skip to content
Advertisement

NodeJs Testrunner freezes / stops when setting up Express server

I’m using Node v18 with the experimental testrunner. I use express as a dev dependency for http integration tests which works fine but there is one test freezing or stopping the testrunner ( it doesn’t continue )

I’m using TS but can also reproduce it with JS, the test file HttpTests.js contains

import assert from 'assert/strict';
import express from 'express';
import test from 'node:test';

test('Http', async () => {
  const server = express();
    
  server.listen(3000);
  assert.ok(false);
});

Running this with the npm script "test": "node --test $(find . -name '*Tests.js')" breaks the test runner.

Any ideas what is wrong or missing?


Why am I not using the default execution model?

Since I’m using TS I had to find a way to use ts-node with the testrunner. You can find more information here

https://github.com/nodejs/node/issues/43675

So currently my TS project is using this npm script, which works fine


Reproduction

I created a minimal reproduction repository with and without TypeScript

For reproduction purposes run mkdir reproduction && cd reproduction && npm init -y && npm install express. After that create a test directory with a file HttpTests.js containing the content as shown above. Change the package.json to

{
  "name": "reproduction",
  "type": "module",
  "scripts": {
    "test": "node --test $(find . -name '*Tests.js')"
  }
}

and run the script, the testrunner should not finish.


The testrunner is still experimental

Yes I know. But there are many tests in the project that work perfectly fine. Some sample code

await t.test('subtest - saves data.', async () => {
    const expectedResult = {};
  
    const api = express();
    const port = await getRandomPort();
    const server = api
        .use(express.json())
        .post('/save', (request, response) => {
            response.json(expectedResult);
        })
        .listen(port);

    const httpDataProvider = new HttpDataProvider({ url: `http://localhost:${port}` });
    const actualResult = await httpDataProvider.saveSomething();

    assert.deepEqual(actualResult, expectedResult);

    server.close();
});

Advertisement

Answer

The issue is the async activity that you start (server.listen()) but don’t stop before the test errors out (by an exception thrown by assert.ok(false)).

Your second test case will probably also stall if actualResult doesn’t deep-equal expectedResult because of the same issue (server.close() won’t be called).

A workaround would be to always make sure the server gets closed in the end:

test('Http', async () => {
  const app    = express();
  const server = app.listen(3000);
  try {
    assert.ok(false);
  } finally {
    server.close();
  }
});

Most test frameworks provide “before/after” functionality that can be used to set up or tear down auxiliary objects before and after a test.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement