Here is a test suite written in Jest (v20.0.4).
The first 3 tests are expected behaviour, my question lies related to Test4.
test('Test1: the list should contain 7', () => {
const data = [1, 2, 7, 9];
expect(data).toContain(7);
});
// > Passes as expected
test('Test2: the list should contain 7', () => {
const data = [1, 2, 7, 9];
expect(data).toContain(8);
});
// > Fails as expected; Expected array: [1, 2, 7, 9] To contain value: 8
test('Test3: the list should contain 7', (done) => {
function callback(data) {
expect(data).toContain(7);
done();
}
setTimeout(() => {
callback([1, 2, 7, 9]);
}, 500);
});
// > Passes as expected
test('Test4: the list should contain 7', (done) => {
function callback(data) {
expect(data).toContain(8);
done();
}
setTimeout(() => {
callback([1, 2, 7, 9]);
}, 500);
});
// > Fails with Error "Timeout - Async callback was not invoked within timeout specified"
Here is my question:
In Test4, done()
is called immediately after the expect
statement.
So, even if the expect statement does not pass, I guessed it should fail with error similar to Test2: (Expected array: [1, 2, 7, 9] To contain value: 8)
However, it fails with a timeout error as shown above which suggests that done()
is never called.
Why? I don't get it!
Can someone please guide me through this behaviour?
I scanned through the docs but could not find anything related to this.
Here is a test suite written in Jest (v20.0.4).
The first 3 tests are expected behaviour, my question lies related to Test4.
test('Test1: the list should contain 7', () => {
const data = [1, 2, 7, 9];
expect(data).toContain(7);
});
// > Passes as expected
test('Test2: the list should contain 7', () => {
const data = [1, 2, 7, 9];
expect(data).toContain(8);
});
// > Fails as expected; Expected array: [1, 2, 7, 9] To contain value: 8
test('Test3: the list should contain 7', (done) => {
function callback(data) {
expect(data).toContain(7);
done();
}
setTimeout(() => {
callback([1, 2, 7, 9]);
}, 500);
});
// > Passes as expected
test('Test4: the list should contain 7', (done) => {
function callback(data) {
expect(data).toContain(8);
done();
}
setTimeout(() => {
callback([1, 2, 7, 9]);
}, 500);
});
// > Fails with Error "Timeout - Async callback was not invoked within timeout specified"
Here is my question:
In Test4, done()
is called immediately after the expect
statement.
So, even if the expect statement does not pass, I guessed it should fail with error similar to Test2: (Expected array: [1, 2, 7, 9] To contain value: 8)
However, it fails with a timeout error as shown above which suggests that done()
is never called.
Why? I don't get it!
Can someone please guide me through this behaviour?
I scanned through the docs but could not find anything related to this.
Share Improve this question edited Feb 15, 2021 at 19:05 ggorlen 57.9k8 gold badges114 silver badges157 bronze badges asked Oct 2, 2018 at 7:42 abrajabraj 3493 silver badges15 bronze badges 1- I believe Jest returns as soon as it sees a failing expect statement. But any ideas regarding how to call done() immediately after failing expect, rather than waiting for Timeout to happen. – abraj Commented Oct 2, 2018 at 7:49
2 Answers
Reset to default 7In your code, the expect
call throws and done
is never reached. As such, Jest waits until it times out and emits the nasty, unclear error you noticed.
The solution is to catch the throw and call done(error)
to indicate to the Jest runner that the test failed.
test('Test4: the list should contain 7', done => {
function callback(data) {
try {
expect(data).toContain(8);
done();
}
catch (err) {
done(err);
}
}
setTimeout(() => {
callback([1, 2, 7, 9]);
}, 500);
});
A run of this gives the desired result:
Error: expect(received).toContain(expected) // indexOf
Expected value: 8
Received array: [1, 2, 7, 9]
From the docs:
If the
expect
statement fails, it throws an error anddone()
is not called. If we want to see in the test log why it failed, we have to wrapexpect
in atry
block and pass the error in thecatch
block todone
. Otherwise, we end up with an opaque timeout error that doesn't show what value was received byexpect(data)
.
I found that setTimeout
and Jest don't work as one might think. But there is a good way to deal with it:
At the start of your test you can tell Jest to use fakeTimers:
jest.useFakeTimers();
When you need the callback to be executed you can trigger it with
jest.runAllTimers();
https://jestjs.io/docs/en/timer-mocks.html