node.jstypescriptexpresstestingtap

NodeJS and ExpressJS app times out with TAP


I'm trying to test an ExpressJS (4.17.8) and NodeJS (16.3) powered server (app) with tap, and later with supertest. First I'm testing the instantiation of the server, and later its routes.

For this, my app is wrapped in a Connector Class that:

So I have a test file like this:

import test, { Test } from "tape-promise/tape";

test("connects to X", async (t: Test) => {

  connector = new Connector();

  await connector.ConnectToExternalSystem(); // connects to external system
  await connector.registerEndpoints(); // e.g., sets to the Express app: app[get](/endpoint)...
  await connector.listen(); // gets stuck?
  t.ok(connector);
  t.end();

My problem is that for every test I perform, tap seems to get stuck (happens with Jest as well) in connector.listen() - leading for the test to timeout.

My project and tests are written in Typescript 4.3.5. I am using the following npm script to run the tests:

"test": "tap --ts --jobs=1 --node-arg=--max-old-space-size=4096 --timeout=15 --branches=45 --functions=70 --lines=75 --statements=75 \"src/test/{unit,integration}/\"",

Is there anything I'm doing wrong? Appreciate your advice on this. Thanks.


Solution

  • Depends on what the implementation of Connector really looks like. Assuming that the .listen() method of it calls express' listen under the hood the issue might be that you are not handling the success callback or that it's not wired up properly to the returned promise via the resolve callback of the promise.

    So something like this could work (rough pseudo code, not tested):

    class Connector {
      listen(): Promise<void> {
        return new Promise((resolve, reject) => {
          this.expressApp.listen((err: Error) => {
             if (err) {
               reject(err);
             } else {
               resolve();
             }
          });
        });
      }
    }
    

    What the above does is ensures that the returned promise resolves once the callback has been invoked (or rejects if the callback was passed in an error which is the standard NodeJS error handling style)