Asynchronous Testing with Jest and TypeScript
Testing asynchronous code can be tricky, as traditional synchronous testing methods don't work well when dealing with promises, timeouts, or other asynchronous operations. This challenge focuses on implementing robust asynchronous testing using Jest and TypeScript, ensuring your asynchronous functions behave as expected. Successfully completing this challenge will allow you to confidently test code that relies on asynchronous operations.
Problem Description
You are tasked with creating a Jest test suite for a TypeScript module containing an asynchronous function called fetchData. This function simulates fetching data from an API and returns a promise that resolves with a string after a specified delay. The test suite should verify that fetchData resolves with the correct data after the expected delay, and that it handles potential errors gracefully.
What needs to be achieved:
- Write Jest tests using
async/awaitor.then()to properly handle the asynchronous nature offetchData. - Verify that the resolved value of the promise returned by
fetchDatais the expected string. - Test the function's behavior when an error is thrown (simulated by a modified version of the function).
- Ensure the tests are readable, maintainable, and follow best practices for asynchronous testing in Jest.
Key Requirements:
- Use Jest's built-in assertion methods (e.g.,
expect). - Handle asynchronous operations correctly to avoid timing issues and race conditions.
- Test both successful and error scenarios.
Expected Behavior:
- The tests should pass when
fetchDataresolves with the correct data. - The tests should fail when
fetchDataresolves with incorrect data or throws an error. - The tests should complete within a reasonable timeframe (avoiding excessively long timeouts).
Edge Cases to Consider:
- Network errors (simulated by throwing an error within
fetchData). - Unexpected data formats (although this is not explicitly tested, consider how your tests would adapt if the data format changed).
- Race conditions if using
.then()instead ofasync/await(thoughasync/awaitmitigates this).
Examples
Example 1:
Input: fetchData(1000) // Fetch data with a 1-second delay
Output: "Data fetched after 1 second"
Explanation: The fetchData function should resolve with the specified string after the given delay.
Example 2:
Input: fetchDataWithError(1000) // Fetch data with a 1-second delay, but throws an error
Output: Test should fail with an error message indicating the error was not handled.
Explanation: The fetchDataWithError function should throw an error after the delay, and the test should verify that this error is caught and handled appropriately.
Constraints
- The
fetchDatafunction should accept a number representing the delay in milliseconds as an argument. - The
fetchDatafunction should return a Promise<string>. - The
fetchDataWithErrorfunction should also accept a number representing the delay in milliseconds and should return a Promise that rejects after the delay. - Tests should complete within 5 seconds.
- Use TypeScript for both the module and the tests.
Notes
- Consider using
async/awaitfor cleaner asynchronous code. - Jest provides features like
setTimeoutmocking anddonecallbacks, but these are generally less preferred thanasync/awaitor.then()for modern asynchronous testing. - Think about how to structure your tests to make them readable and maintainable. Separate test cases for success and failure scenarios.
- The provided code for
fetchDataandfetchDataWithErroris below. You are only responsible for writing the tests.
// fetchData.ts
export function fetchData(delay: number): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Data fetched after " + delay + " milliseconds");
}, delay);
});
}
export function fetchDataWithError(delay: number): Promise<string> {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error("Failed to fetch data"));
}, delay);
});
}