Testing Asynchronous Code with Promises in Jest
Testing asynchronous code, particularly code that relies on Promises, is a crucial skill for any TypeScript developer. This challenge focuses on writing effective Jest tests for functions that return Promises, ensuring your asynchronous operations behave as expected. Successfully completing this challenge will solidify your understanding of Jest's asynchronous testing capabilities.
Problem Description
You are tasked with writing Jest tests for a function called fetchData. This function takes a URL as input and returns a Promise that resolves with the JSON data fetched from that URL. The function simulates a network request with a delay. Your goal is to write Jest tests that verify the following:
- Successful Fetch: The Promise resolves with the expected JSON data when the URL is valid.
- Error Handling: The Promise rejects with an error when the URL is invalid (simulated by returning an error in the mock fetch).
- Timeout Handling: The Promise rejects with a timeout error if the fetch takes longer than a specified timeout duration.
You will be provided with a mock fetch function to simulate network requests. You need to use jest.fn() to mock the fetch function and control its behavior within your tests. You should use async/await for cleaner asynchronous test code.
Examples
Example 1: Successful Fetch
Input: fetchData('https://example.com/data')
Output: { data: { message: 'Data fetched successfully!' } }
Explanation: The mock fetch resolves with a JSON object containing a 'message' property. The fetchData function then returns this object wrapped in a Promise.
Example 2: Error Handling
Input: fetchData('https://invalid-url')
Output: Error: Request failed
Explanation: The mock fetch rejects with an error. The fetchData function catches this error and returns it wrapped in a Promise.
Example 3: Timeout Handling
Input: fetchData('https://slow-url', 500)
Output: Error: Request timed out after 500ms
Explanation: The mock fetch takes longer than 500ms to resolve. The fetchData function times out and rejects with a timeout error.
Constraints
- The
fetchDatafunction should accept a URL string and an optional timeout in milliseconds as arguments. If no timeout is provided, it defaults to 1000ms. - The mock
fetchfunction should be mocked usingjest.fn(). - Tests should use
async/awaitfor readability. - The timeout error message should be "Request timed out after [timeout]ms".
- The error message for a failed request should be "Request failed".
- You are provided with a basic
fetchDatafunction skeleton. Do not modify the function itself, only write the tests.
Notes
- Consider using
jest.setTimeout()to increase the default timeout for your tests if necessary. - Use
expect().resolvesandexpect().rejectsfor testing Promise resolutions and rejections, respectively. - Pay close attention to the order of operations and how the mock
fetchfunction's behavior affects the Promise's resolution or rejection. - Think about how to properly mock the
fetchfunction to simulate both successful and error scenarios.
// fetchData.ts (Do not modify this file)
async function fetchData(url: string, timeout: number = 1000): Promise<any> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error("Request timed out after " + timeout + "ms"));
}, timeout);
fetch(url)
.then(response => {
clearTimeout(timer);
if (!response.ok) {
throw new Error("Request failed");
}
return response.json();
})
.then(data => {
resolve({ data });
})
.catch(error => {
clearTimeout(timer);
reject(error);
});
});
}
export default fetchData;
// fetchData.test.ts (Your test file)
import fetchData from './fetchData';
global.fetch = jest.fn(); // Mock the global fetch function
describe('fetchData', () => {
// Your tests go here
});