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
- https://github.com/matthiashermsen/reproduce-broken-test-ts
- https://github.com/matthiashermsen/reproduce-broken-test-js
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.