Testing async code
Jest needs to know if it's running async code in order to wait for it to complete.
Callbacks
function getData(cb) {
/* faking async code */
const data = {
name: 'bob',
age: 42,
}
setTimeout(() => {
cb(data)
}, 100)
}
test('using callbacks in jest', () => {
function callback(data) {
expect(data).toMatchObject({ name: 'NOT BOB' })
}
getData(callback)
})
note:
toMatchObject
is a built-in matcher that checks for an object to be a subobject of what passed toexpect
PASS ./test.js
✓ using callbacks in jest
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.55s, estimated 1s
Ran all test suites.
Watch Usage: Press w to show more.
the above test will pass making no assertions at all, because Jest does not know that has to wait for the callback passed to getData
to be called.
To overcome this we can declare that we use the done
argument passed by Jest to our function. This will tell Jest to wait until we call done
explicitly
test('using callbacks in jest', done => {
function callback(data) {
expect(data).toMatchObject({ name: 'NOT BOB' })
done()
}
getData(callback)
})
now the expectation is called and the test fails as expected
FAIL ./test.js
✕ using callbacks in jest (111ms)
● using callbacks in jest
expect(received).toMatchObject(expected)
- Expected
+ Received
Object {
- "name": "NOT BOB",
+ "name": "bob",
}
13 | test('using callbacks in jest', done => {
14 | function callback(data) {
> 15 | expect(data).toMatchObject({ name: 'NOT BOB' })
| ^
16 | done()
17 | }
18 | getData(callback)
at toMatchObject (test.js:15:18)
at cb (test.js:9:5)
Promises
function getData() {
/* faking async code */
const data = {
name: 'bob',
age: 42,
}
return Promise.resolve(data)
}
test('using promises in jest', () => {
return getData().then(data => {
expect(data).toMatchObject({ name: 'not bob' })
})
})
Note: using promises we cannot use the
done
argument but we must return a promise
FAIL ./test.js
✕ using promises in jest (4ms)
● using promises in jest
expect(received).toMatchObject(expected)
- Expected
+ Received
Object {
- "name": "not bob",
+ "name": "bob",
}
11 | test('using promises in jest', () => {
12 | return getData().then(data => {
> 13 | expect(data).toMatchObject({ name: 'not bob' })
| ^
14 | })
15 | })
16 |
at toMatchObject (test.js:13:18)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.439s, estimated 1s
Ran all test suites.
the built-in matcher modifier resolves
is also available to make promises assertion a bit nicer
test('using promises in jest', () => {
return expect(getData()).resolves.toMatchObject({ name: 'bob' })
})
Rejections
to test rejections .catch
can be chained to a promise or the built-in modifier rejects
can be used
function getData() {
/* faking async code */
return Promise.reject({
error: 'no data',
})
}
test('using rejected promises with catch', () => {
return getData().catch(data => {
expect(data).toMatchObject({ error: 'no data' })
})
})
test('using rejected promises with rejects', () => {
return expect(getData()).rejects.toMatchObject({ error: 'no data' })
})
PASS ./test.js
✓ using rejected promises with catch
✓ using rejected promises with rejects (1ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.483s, estimated 1s
Ran all test suites.
Watch Usage: Press w to show more.
Using async/await
To use async/await
is enough to change the test function to be async
. It also works when using resolves
function getData() {
/* faking async code */
const data = {
name: 'bob',
age: 42,
}
return Promise.resolve(data)
}
test('using promises with async', async () => {
const data = await getData()
expect(data).toMatchObject({ name: 'bob' })
})
test('using resolves with async', async () => {
await expect(getData()).resolves.toMatchObject({ name: 'bob' })
})
Author: Jaga Santagostino