Hone logo
Hone
Problems

Orchestrating Asynchronous Tests with Jest

This challenge focuses on effectively managing and coordinating multiple asynchronous operations within Jest tests. You'll learn how to ensure tests run in the correct sequence, handle dependencies between asynchronous tasks, and produce reliable test results for complex scenarios. This is crucial for testing applications that rely heavily on network requests, database interactions, or other time-consuming operations.

Problem Description

Your task is to create a Jest test suite that orchestrates a series of asynchronous operations. Specifically, you will need to simulate fetching data from two different API endpoints, processing the data from the first endpoint to determine parameters for the second, and then asserting the final combined result.

Key Requirements:

  1. Sequential Asynchronous Execution: Ensure that fetchUserData completes before fetchUserPosts begins.
  2. Parameter Dependency: The userId returned by fetchUserData must be used as a parameter for fetchUserPosts.
  3. Data Aggregation: Combine the user data and their posts into a single object.
  4. Assertion: Verify the structure and content of the aggregated data.
  5. Mocking: Use Jest mocks to simulate the behavior of the asynchronous API calls.

Expected Behavior:

The test should simulate a scenario where fetching user information is a prerequisite for fetching that user's posts. The final test should assert that the correct user data and their associated posts are returned, demonstrating successful orchestration.

Edge Cases to Consider:

  • What happens if fetchUserData fails (e.g., returns an error)? (For this challenge, we will assume successful API calls for simplicity, but in a real-world scenario, error handling would be paramount.)
  • What if fetchUserPosts returns an empty array of posts?

Examples

Example 1: Successful Data Fetching and Aggregation

Imagine we have functions to simulate fetching user data and their posts.

// Simulated API functions (these will be mocked in the test)
async function fetchUserData(username: string): Promise<{ id: number; name: string }> {
  // Simulate network delay
  await new Promise(resolve => setTimeout(resolve, 100));
  if (username === "johndoe") {
    return { id: 123, name: "John Doe" };
  }
  throw new Error("User not found");
}

async function fetchUserPosts(userId: number): Promise<{ postId: number; title: string }[]> {
  // Simulate network delay
  await new Promise(resolve => setTimeout(resolve, 100));
  if (userId === 123) {
    return [
      { postId: 1, title: "My First Post" },
      { postId: 2, title: "Another Great Article" },
    ];
  }
  return [];
}

Test Scenario: Fetch data for "johndoe", then use their ID to fetch their posts, and combine the results.

Input to the Orchestration Logic (within the test): A username, e.g., "johndoe".

Expected Output of the Orchestration Logic (within the test):

{
  "userData": {
    "id": 123,
    "name": "John Doe"
  },
  "posts": [
    { "postId": 1, "title": "My First Post" },
    { "postId": 2, "title": "Another Great Article" }
  ]
}

Explanation:

The test will first mock fetchUserData to return the data for "johndoe". Once this mock resolves, the test will extract the id (123). Then, it will mock fetchUserPosts to be called with userId: 123 and return the corresponding posts. Finally, it will assert that the aggregated object matches the expected structure and content.

Constraints

  • The asynchronous operations (fetchUserData, fetchUserPosts) will always resolve successfully within the scope of this challenge.
  • Input usernames for fetchUserData will be valid strings.
  • The test should complete within a reasonable time, implying efficient mocking and asynchronous handling.

Notes

  • Consider using jest.fn() to create mock functions.
  • The mockResolvedValueOnce() or mockImplementationOnce() methods of Jest mock functions will be useful for simulating successful asynchronous API responses.
  • You will need to chain your asynchronous calls correctly using async/await or .then() to ensure proper sequencing.
  • Think about how to pass the result of one asynchronous operation as an argument to the next.
Loading editor...
typescript