Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is done required in async Jest tests?

I'm having an argument with a co-worker about done() in Jest tests.

He's orders of magnitude more experienced with JavaScript than I am, and I came in after async/await was generally accepted, plus came from a .NET environment, so I was used to it.

I write my tests like this:

it("should return 200 OK for POST method", async () => {
    await request(app).post("SOMEENDPOINT")
      .attach("file", "file")
      .expect(200);
});

He's more used to promises so will write his tests like this:

it("should return 200 OK for POST method", (done) => {
  request(app).post("SOMEENDPOINT")
    .attach("file", "file")
    .expect(200, done);
});

He's fine with my push to async/await, but insists I have to include done, such that I'm either doing his version modified version:

it("should return 200 OK for POST method", async (done) => {
  await request(app).post("SOMEENDPOINT")
    .attach("file", "file")
    .expect(200, done);
});

Or:

it("should return 200 OK for POST method", async (done) => {
    const res = await request(app).post("SOMEENDPOINT")
     .attach("file", "file");
    
    expect(res.status).toBe(200);
    done();
});

While I recognize calling done() is entirely necessary when it's included as a parameter, I was under the impression it is wholly unnecessary when using async/await in this context.

Request is supertest.request.

My question is, do I need to use done at all with async/await?

like image 868
Jason St. Jacques Avatar asked Nov 05 '19 14:11

Jason St. Jacques


People also ask

What does done () do in Jest?

Instead of putting the test in a function with an empty argument, use a single argument called done . Jest will wait until the done callback is called before finishing the test. If done() is never called, the test will fail, which is what you want to happen.

How do I use async await In Jest?

To enable async/await in your project, install @babel/preset-env and enable the feature in your babel. config. js file.

How can you wrap a function in async testing?

fakeAsync and tick Like async we wrap the test spec function in a function called fakeAsync . We call tick() when there are pending asynchronous activities we want to complete. Like the async function the fakeAsync function executes the code inside its body in a special fake async test zone.


1 Answers

There's never a need for done and async on the same test function. Pick one or the other, or, in this case, return the promise directly:

it("should return 200 OK for POST method", () =>
  request(app).post("SOMEENDPOINT")
    .attach("file", "file")
    .expect(200)
);

Jest will await any returned promise which means we can always manually construct and return a promise without async or done. This is redundant since we already have a promise at hand, but the following pattern is possible, if only for illustrative purposes:

it("should return 200 OK for POST method", () => {
  return new Promise((resolve, reject) => {
    request(app).post("SOMEENDPOINT")
      .attach("file", "file")
      .expect(200, resolve)
      .catch(err => reject(err))
    ;
  });
});

done is typically used for testing asynchronous callbacks (think basic Node library utilities in the fs module). In these cases, it's more elegant to add the done parameter and invoke it in the callback than it is to promisify the callback by hand. Essentially, done is a shortcut to promisification that abstracts out the new Promise boilerplate shown above.

Note that done can accept a parameter which is treated as an error (see the docs). This should go into any catch blocks to avoid a timeout and confusing error when the main line code throws before calling done:

it("should return 200 OK for POST method", done => {
  request(app).post("SOMEENDPOINT")
    .attach("file", "file")
    .expect(200, done)
    .catch(err => done(err))
  ;
});
like image 80
ggorlen Avatar answered Oct 19 '22 07:10

ggorlen