Reputation: 814
I have a simple code that resolves after 1 second:
const promiseWithTimeout = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({id: 3});
}, 1000);
});
};
I am trying to make a test using jest, however, using resolves or awaits makes it so the test passes even with the wrong expectation (unhandled rejection) . And If I try to make a failing test, it passes even if the expectation fails
What I tried
it("Should return the right ID", ()=>{
expect.assertions(1);
expect(promiseWithTimeout()).resolves.toEqual({id: 3}); // this one works fine
jest.advanceTimersByTime(1000)
})
it("Should return the right ID", ()=>{
expect.assertions(1);
expect(promiseWithTimeout()).resolves.toEqual({id: 4}); // this PASSES , but shows
// UnhandledPromiseRejectionWarning: Error: expect(received).resolves.toEqual(expected) // deep equality
jest.advanceTimersByTime(1000)
})
I expected it to fail and show that the objects are different.
I also tried using then instead, but still same problem
Upvotes: 3
Views: 6263
Reputation: 135227
You have a few options -
return a promise from your test
This will pass -
it("should return the id", () => {
return promiseWithTimeout().then(m => { // return
expect(m.id).toBe(3) // ✓
})
})
This will fail -
it("should return the id", () => {
return promiseWithTimeout().then(m => { // return
expect(m.id).toBe(4) // ✕ id 4
})
})
async..await
This will pass -
it("should return the id", async () => { // async
const m = await promiseWithTimeout() // await
expect(m.id).toBe(3) // ✓
})
This will fail -
it("should return the id", async () => {
const m = await promiseWithTimeout()
expect(m.id).toBe(4) // ✕ id 4
})
.resolves and .rejects
Notice you must return
then expectant promise. This will pass -
it("should return the id", ()=>{
return expect(promiseWithTimeout()).resolves.toEqual({id: 3}) // ✓
})
This will fail -
it("should return the id", ()=>{
return expect(promiseWithTimeout()).resolves.toEqual({id: 4}) // ✕ id 4
})
using jest.advanceTimersByTime
Jest allows you to "fake" the timers so test can run fast while still ensuring async code is behaving correctly. In your testing file you must include jest.useFakeTimers()
and your tests must use one of the techniques above. Here we return
expectant promises -
jest.useFakeTimers()
it("should return the id", () => {
const p = promiseWithTimeout()
jest.advanceTimersByTime(1000) // advance timers
return expect(p).resolves.toEqual({ id: 3 }) // ✓
})
This will fail -
jest.useFakeTimers()
it("should return the id", () => {
const p = promiseWithTimeout()
jest.advanceTimersByTime(1000)
return expect(p).resolves.toEqual({ id: 4 }) // ✕ id 4
})
using expect.assertions
In many cases you do not need to specify expect.assertions
. For example, when using the .resolves
expectation, we will get an assertion failure if the promise rejects -
const promiseWithTimeout = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject({ id: 3 }) // reject
}, 1000)
})
}
jest.useFakeTimers()
it("should return the id", () => {
const p = promiseWithTimeout()
jest.advanceTimersByTime(1000)
return expect(p).resolves.toEqual({ id: 3 }) // ✕ rejected
})
If you expect a promise to be rejected, use the .catch method
and then add expect.assertions
to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test -
test('the fetch fails with an error', () => {
expect.assertions(1) // expect assertions
return fetchData().catch(e => expect(e).toMatch('error')) // ✓
})
For more information, see Testing Asynchronous Code from the Jest docs.
Upvotes: 4