supertest icon indicating copy to clipboard operation
supertest copied to clipboard

Promises makes jest test time out

Open olaven opened this issue 4 years ago • 22 comments

Hello 👋 Thank you for maintaining this project.

I believe I am experiencing a bug where awaiting a call to get response causes timeout.

Minimal example:

it("TIMEOUT when awaiting", async () => {

    await supertest(app)
        .get("/users/me")
});

it("TIMEOUT when using .then", (done) => {

    supertest(app)
        .get("/users/me")
        .then(response => {

            done();
        });
});

it("WORKING when not using promises at all", () => {

    supertest(app)
        .get("/users/me");
});

The two first tests are failing with the following message: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:

Please let me know if I have left out any useful information.

All the best! Olav

olaven avatar Jan 10 '21 21:01 olaven

Are you getting same behavior with async/await ?

tosiek88 avatar Jan 11 '21 15:01 tosiek88

@tosiek88 Yes I am. I believe the first test shows this. Or are you referring to something else? 🙂

olaven avatar Jan 11 '21 15:01 olaven

same here

bogdanned avatar Jan 30 '21 16:01 bogdanned

I have the same problem with async/await.

Using the following versions: "jest": "26.6.3", "supertest": "3.4.2",

BalazsSevecsek avatar Apr 07 '21 10:04 BalazsSevecsek

@olaven @bogdanned @BalazsSevecsek Does there happen to be a runtime error in the app itself (not the tests) in your cases? Cf. https://github.com/mochajs/mocha/issues/4632

codingthat avatar Apr 28 '21 11:04 codingthat

same problem here..jest not working with async/await with supertest..without async await all tests pass even when they supposed to fail

mkimbo avatar Jun 12 '21 13:06 mkimbo

@olaven when running the test without promises at all, I also get the following warning:

A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks.

Besides that I can also confirm that the bug also occurs when using the agent:

const agent = supertest.agent(app);

it("TIMEOUT when awaiting with agent", async () => {

    await agent.get("/users/me")
});

it("TIMEOUT when using .then", (done) => {

    agent
        .get("/users/me")
        .then(response => {

            done();
        });
});

it("WORKING when not using promises at all", () => {

    agent.get("/users/me");
});

luukvhoudt avatar Jun 30 '21 17:06 luukvhoudt

I can't say for sure if my issue is the same, but I have a similar issue. In my case, I always get the below warning from Jest when I use the supertest request's post method even without using any async/await/promises.

Jest did not exit one second after the test run has completed. This usually means that there are asynchronous operations that weren't stopped in your tests.

The test code is like this:

    it("POST /api/notes adds the note", (done) => {
        request(app)
            .post("/api/notes")
            .send({
                "content": "some note",
                "notebookId": "0"
            }).expect(200)
            .expect(resp => resp.body.content === "some note")
            .then(_ => {
                request(app)
                .get("/api/notes")
                .expect(resp => resp.body.length === notes.length + 1);
                done();
            }).catch(err => done(err));
    });

aderchox avatar Dec 17 '21 13:12 aderchox

Hi, I have encountered the same problem and I have narrowed it down to when you simply have a promise that has been put on the call stack.

So these don't pose a problem:


it('FIRST', () => Promise.resolve(true));

it('SECOND', () => Promise.resolve(true));

but as soon as you introduce a timeout in the mix, it breaks (the second test times out):


it('FIRST', () => new Promise((r) => setTimeout(r)));

it('SECOND', () => new Promise((r) => setTimeout(r)));

Even if I try to follow the way the documentation describes and use .resolves, the problem persists:


const asyncfn = (message: string) => new Promise((r) => setTimeout(() => r(message)));

it('FIRST', () => expect(asyncfn('first')).resolves.toBe('first'));

it('SECOND', () => expect(asyncfn('second')).resolves.toBe('second'));

Tokimon avatar Mar 17 '22 14:03 Tokimon

I also experienced this issue.

iliaskarim avatar Mar 17 '22 16:03 iliaskarim

Same here.

premekholasek avatar Apr 04 '22 14:04 premekholasek

same here! this is happening for me too

1forh avatar Apr 06 '22 20:04 1forh

Apparently, in my case, I can't use ES6 form to import/export modules.

So, I create a file called testServer.ts with the same code as my default server (server.ts), but using module.exports instead export default.

const app = express()

app.use(cors())
app.use(helmet())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(express.json())
app.use(routes)

module.exports = app

In my test file I used require to import the test server, and I used async/await syntax.

import "reflect-metadata"
import request from "supertest"
import AppDataSource from "../../../database/dataSource"
const app = require("../../../utils/testServer")

describe("getAllProductsController", () => {
  describe("GET /products", () => {
    beforeAll(async () => {
      await AppDataSource.initialize()
    })

    afterAll(async () => {
      await AppDataSource.destroy()
    })
    it("should return 200 and valid response if request param list is empty", async () => {
      const response = await request(app).get("/api/products")
      console.log(response.status)
      expect(response.status).toEqual(200)
    })
  })
})

I still intend to improve this, but for now it has given me peace of mind.

digzom avatar May 23 '22 09:05 digzom

I'm not sure this is related to the reported or known behaviour, but looked similar in my case and might help others stumbling over this.

For me jest.useFakeTimers() caused supertest to break existing test cases by timing out. I didn't have time to dig into the cause of this, but given that fakeTimers is quite invasive I wouldn't consider this a supertest/superagent bug.

mojadev avatar Jun 05 '22 08:06 mojadev

Hi, I have encountered the same problem and I have narrowed it down to when you simply have a promise that has been put on the call stack.

So these don't pose a problem:

it('FIRST', () => Promise.resolve(true));

it('SECOND', () => Promise.resolve(true));

but as soon as you introduce a timeout in the mix, it breaks (the second test times out):

it('FIRST', () => new Promise((r) => setTimeout(r)));

it('SECOND', () => new Promise((r) => setTimeout(r)));

Even if I try to follow the way the documentation describes and use .resolves, the problem persists:

const asyncfn = (message: string) => new Promise((r) => setTimeout(() => r(message)));

it('FIRST', () => expect(asyncfn('first')).resolves.toBe('first'));

it('SECOND', () => expect(asyncfn('second')).resolves.toBe('second'));

@Tokimon these work for me..

miparnisari avatar Jul 05 '22 23:07 miparnisari

I had a similar problem. I needed to wrap the await call within try/catch block. In the catch block you can get the error and you should use the done() in the catch block as well.

rviktor87 avatar Jul 14 '22 09:07 rviktor87

On a project of mine, I was able to work around this by ensuring listen was not called as part of importing the Express app that is passed to request. Essentially, the listen call is done in index.js (our entrypoint) and the Express app is created in app.js. index.js imports app.js. The tests only ever import app.js.

LoganBarnett avatar Nov 11 '22 19:11 LoganBarnett

Hello, I was facing the same problem. The problem was require('express'). This returns the defaults express app object and contains none of the configuration for your app.

Change it as follows: const app = require('./app');

This will now run jest with your app.ts server and there won't be a timeout.

hariskamran avatar Apr 03 '23 08:04 hariskamran

I'm not sure this is related to the reported or known behaviour, but looked similar in my case and might help others stumbling over this.

For me jest.useFakeTimers() caused supertest to break existing test cases by timing out. I didn't have time to dig into the cause of this, but given that fakeTimers is quite invasive I wouldn't consider this a supertest/superagent bug.

oh wow. You saved me tons of time! Thanks

1nstinct avatar Apr 11 '23 18:04 1nstinct

I managed to get it working by adding a finally block:

    test('endpoint works', (done) => {
        void supertestInstance
            .get('my/endpoint')
            .expect(200)
            .then((res: any) => {
                // expect stuff
            }).finally(() => done());
    });

oliveirarthur avatar Dec 08 '23 01:12 oliveirarthur

I'm not sure this is related to the reported or known behaviour, but looked similar in my case and might help others stumbling over this.

For me jest.useFakeTimers() caused supertest to break existing test cases by timing out. I didn't have time to dig into the cause of this, but given that fakeTimers is quite invasive I wouldn't consider this a supertest/superagent bug.

That's correct. In my case, the fake timer also caused the problem. After using the real time, the problems disappeared.

hylickipiotr avatar Jan 11 '24 08:01 hylickipiotr