
- Jest - Getting Started
- Jest - Core Concepts
- Jest - Mocking
- Jest - Advanced Testing
- Jest - React Testing
- Jest - Configuration
- Jest - Special Testing Scenarios
- Jest - Performance & Advanced Topics
- Jest - Integrations
- Jest - Best Practices
- Jest - Deployment and CI/CD
- Jest Resources
- Jest - Useful Resources
- Jest - Discussion
Jest - Advanced Testing
In this chapter, we'll cover testing asynchronous code in Jest. Asynchronous tasks, like API calls, timers, and file operations, are common in JavaScript. Jest offers simple ways to test them. Below are the topics we will cover:
- Jest - Asynchronous Testing
- Jest - Promise Testing
- Jest - Async/Await Testing
- Jest - Callback Testing
Jest - Asynchronous Testing
Asynchronous testing is used to test code that involves tasks like API calls, timers, or file operations, which take time to complete. In JavaScript, common asynchronous operations include:
- setTimeout: Delays code execution.
- HTTP requests (using fetch or axios): Wait for server responses.
- Promises: Handle future values that aren't immediately available.
In Jest, asynchronous testing makes sure your tests wait for these tasks to finish before making assertions, preventing premature checks that could lead to incorrect results.
Jest supports different approaches for asynchronous testing:
- Callback-based Code: Use done() to signal that the test is complete.
- Promise-based Code: Use resolves and rejects to test promise outcomes.
- Async/Await: A modern, cleaner syntax for handling promises, improving readability and structure.
Jest - Promise Testing
A Promise represents the final outcome of an asynchronous operation, either resolving (success) or rejecting (failure).
To test a function that returns a promise, simply return the promise from your test. Jest will automatically wait for it to resolve or reject.
Testing a Resolved Promise
When testing a resolved promise, you expect it to succeed and return a value. This can be done using .then() or Jest's .resolves matcher.
const fetchData = () => Promise.resolve('Data received'); test('fetchData resolves with correct value', () => { return expect(fetchData()).resolves.toBe('Data received'); });
Testing a Rejected Promise
When testing a rejected promise, you expect it to fail and return an error or rejection message. This can be done using .catch() or Jest's .rejects matcher.
const fetchError = () => Promise.reject('Error occurred'); test('fetchError rejects with correct message', () => { return expect(fetchError()).rejects.toBe('Error occurred'); });
Jest - Async/Await Testing
async and await are JavaScript features that allow you to write asynchronous code in a synchronous-like manner. This makes the code more readable and easier to test.
In Jest, you can use async and await to test asynchronous functions, making the test wait for the promise to resolve before proceeding.
Testing an Async Function
To test async functions, you can use await. Jest will automatically wait for the promise to resolve or reject before making assertions.
const fetchData = async () => 'Data received'; test('fetchData resolves with the correct value', async () => { const data = await fetchData(); expect(data).toBe('Data received'); });
Testing Async Functions that Throw Errors
If an async function might throw an error, you can use await with expect().rejects to test the rejection.
const fetchError = async () => { throw new Error('Something went wrong'); }; test('fetchError throws an error', async () => { await expect(fetchError()).rejects.toThrow('Something went wrong'); });
Using expect.assertions()
When testing async functions that throw errors, use expect.assertions() to make sure all assertions are made before the test finishes.
test('fetchError throws an error', async () => { expect.assertions(1); // Make sure at least one assertion runs try { await fetchError(); // Call the async function } catch (error) { expect(error).toMatch('Something went wrong'); // Check if the error is as expected } });
Combining async/await with .resolves and .rejects
You can combine async/await with Jest's .resolves and .rejects matchers to make your tests cleaner and avoid manual promise handling.
Using .resolves with async/await
test('fetchData resolves with correct value', async () => { await expect(fetchData()).resolves.toBe('Data received'); });
Using .rejects with async/await
test('fetchError rejects with the correct message', async () => { await expect(fetchError()).rejects.toThrow('Something went wrong'); });
Jest - Callback Testing
When testing functions with callbacks in Jest(functions passed as arguments to other functions to be executed later), you need to ensure the test waits for the callback to be called before it completes.
How to Test Callbacks in Jest?
In Jest, you can test asynchronous functions that use callbacks by using the done() function. You pass done as a parameter to your test, and then call done() inside the callback to let Jest know when the test is complete.
Testing a Callback
Let's say we have a function fetchDataFromServer that takes a callback and simulates fetching data with a delay.
const fetchDataFromServer = (callback) => { setTimeout(() => { callback('Server Response: Success'); }, 1000); // Simulates a 1-second delay };
Now, let's write a test to check if the callback receives the correct data:
test('fetchDataFromServer should return correct data', done => { const callback = (data) => { expect(data).toBe('Server Response: Success'); // Check if the data is correct done(); // Tell Jest the test is complete }; fetchDataFromServer(callback); // Call the function with the callback });
Common Mistake: Forgetting done()
If you forget to call done(), Jest will finish the test too early and give a timeout error, because it doesn't know the test is still running. Here's an example of a test that would fail:
test('fetchDataFromServer should return correct data', () => { const callback = (data) => { expect(data).toBe('Server Response: Success'); }; fetchDataFromServer(callback); // Test finishes too early, before callback });
This will fail because Jest finishes the test before the callback is executed, leading to a timeout error.