Skip to content

Instantly share code, notes, and snippets.

@adamesque
Last active November 8, 2017 17:16
Show Gist options
  • Save adamesque/b282760d5eeeb8f8aac20381483ad349 to your computer and use it in GitHub Desktop.
Save adamesque/b282760d5eeeb8f8aac20381483ad349 to your computer and use it in GitHub Desktop.
Jest timeout explorations (AKA I don't understand timeouts) (AKA okay I figured it out)
async function wait(ms) {
return new Promise((resolve) => {
window.setTimeout(() => resolve(), ms);
});
}
describe('Jest Timeout test', () => {
beforeEach(() => {
// Calling `jest.setTimeout` simply mutates `jasmine.DEFAULT_TIMEOUT_INTERVAL`
jest.setTimeout(200);
});
/**
* This test passes. Our timeout, set by the beforeEach, is 200ms, and we're
* only waiting 100ms.
*/
it('does not timeout when you act fast', async () => {
await wait(100);
expect(true).toBe(true);
});
/**
* We expect this to fail, and it does, with:
* `Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.`
*
* Not sure how to assert that it should though!
*/
it('times out after the specified interval', async () => {
await wait(400);
expect(true).toBe(true);
});
/**
* This test also passes. Remember, the way async works in JavaScript is that
* an async function returns the instant it hits an `await`, so setting the timeout
* here is setting the amount of time Jest will wait for you to call the `done()`
* callback or for your promise to resolve _after_ your function returns.
*/
it('allows you to change the timeout for a specific test', async () => {
jest.setTimeout(500);
await wait(400);
expect(true).toBe(true);
});
/**
* With the previous explanation in mind, this isn't weird at all. There's no
* timer running while your test is blocking and executing… the timer starts
* as soon as you return
*/
it("weirdly doesn't fail when we block before doing something async", async () => {
jest.setTimeout(500);
// Block for a few seconds
for (let i = 0; i < 10000000; i++) {
let x = Math.sqrt(2);
}
// This test function returns, starting a 500ms timer, right here.
// Everything after the `await` is executed later
await wait(400);
expect(true).toBe(true);
});
/**
* This test passes for the same reason the last one does. No mystery here!
*/
it("doesn't even fail when we block before doing something async using the callback form", (
done
) => {
jest.setTimeout(500);
// Block for a few seconds
for (let i = 0; i < 10000000; i++) {
let x = Math.sqrt(2);
}
// This `wait.then` chain executes immediately. It does not block, and so
// we reach the bottom of our `it()` function and return, starting our
// 500 ms timer.
wait(400).then(() => {
expect(true).toBe(true);
done();
});
});
/**
* This is being done by our `beforeEach`. If we remove that, this test fails
* because the timeout is just a global var scoped to this test file.
*/
it('resets the timeout after each test?', () => {
expect(jasmine.DEFAULT_TIMEOUT_INTERVAL).toEqual(200);
});
/**
* This test will fail. The only timeout that matters is the one set before
* your first `await`.
*/
it('really really lets you change it on the fly?', async () => {
// If you set this to 1100 to account for all our waits, then the test passes.
jest.setTimeout(1000);
await wait(900);
// Setting a timeout after our first `await` has no effect, because by
// this point our test function has already returned and our callback/async
// timer is set.
//
// We can't change it from inside the promise chain because the initial value
// was probably baked into a call to `window.setTimeout` made by the test
// runner when our test function actually returned up above.
jest.setTimeout(1200);
await wait(100);
expect(true).toBe(true);
});
});
@adamesque
Copy link
Author

I initially made the mistake of thinking the async timer somehow started before each async test/function was kicked off. But that's not how async works in JavaScript; async functions block while executing but normally return essentially immediately.

Once Jest knows a test function is async, it'll execute it, and then wait TIMEOUTms for a callback to be invoked or promise to be resolved before moving on to the next test. So that's why changing the timeout inside an async test function works; the value used is whatever that global is set to the instant the test function returns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment