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
?
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.
To enable async/await in your project, install @babel/preset-env and enable the feature in your babel. config. js file.
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.
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))
;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With