Hone logo
Hone
Problems

Building Reusable Test Utilities in Jest (TypeScript)

Writing tests can be repetitive, especially when dealing with common setup, assertions, or helper functions. This challenge focuses on creating a shared utility file in TypeScript that can be imported into multiple Jest test files to reduce duplication and improve test maintainability. You'll design and implement a utility file containing functions for common testing tasks, demonstrating a clean and reusable approach.

Problem Description

You are tasked with creating a test-utils.ts file that contains reusable functions for common testing scenarios within a TypeScript project using Jest. This file should include functions for:

  1. Mocking API calls: A function that takes a URL and a response body, and mocks fetch to return the specified response.
  2. Deep Equality Assertion: A function that performs a deep equality check between two objects, handling nested structures. This is useful when comparing complex data structures returned from API calls or component props.
  3. Waiting for a Promise: A function that waits for a promise to resolve or reject, with a configurable timeout. This is crucial for testing asynchronous operations.
  4. Component Rendering Helper: A function that takes a React component and props, renders it using render from @testing-library/react, and returns the resulting renderResult.

Key Requirements:

  • The test-utils.ts file should be well-structured and documented.
  • The functions should be generic and reusable across different test files.
  • Error handling should be considered (e.g., timeout errors in the promise waiting function).
  • The mocking function should correctly mock the fetch global.
  • The deep equality assertion should handle circular references gracefully (avoiding stack overflow errors).

Expected Behavior:

  • The mockFetch function should successfully mock fetch and return the specified response.
  • The deepEquals function should correctly identify whether two objects are deeply equal.
  • The waitForPromise function should wait for the promise to resolve or reject within the specified timeout.
  • The renderComponent function should render the component and return the renderResult object.

Edge Cases to Consider:

  • mockFetch: What happens if fetch is not defined (e.g., in a Node.js environment)?
  • deepEquals: How to handle circular references in the objects being compared?
  • waitForPromise: What happens if the promise times out?
  • renderComponent: What happens if the component fails to render?

Examples

Example 1: Mocking API Calls

Input: mockFetch('/api/data', { json: { message: 'Success' } });
Output: (fetch is mocked globally)
Explanation: The `mockFetch` function replaces the global `fetch` with a mock implementation that returns a promise resolving to the provided response.

Example 2: Deep Equality Assertion

Input: deepEquals({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } });
Output: true
Explanation: The `deepEquals` function correctly identifies that the two objects are deeply equal.

Input: deepEquals({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 3 } });
Output: false
Explanation: The `deepEquals` function correctly identifies that the two objects are not deeply equal.

Example 3: Waiting for a Promise

Input: waitForPromise(new Promise<string>((resolve) => setTimeout(() => resolve('Resolved!'), 500)), 1000);
Output: 'Resolved!'
Explanation: The `waitForPromise` function waits for the promise to resolve within the 1000ms timeout.

Input: waitForPromise(new Promise<string>((resolve, reject) => setTimeout(() => reject(new Error('Timeout')), 2000)), 1000);
Output: Error: Timeout
Explanation: The `waitForPromise` function throws a timeout error because the promise rejects after 2000ms, exceeding the 1000ms timeout.

Constraints

  • The mockFetch function should only mock the fetch global within the test environment.
  • The deepEquals function should handle objects with up to 10 levels of nesting without causing a stack overflow.
  • The waitForPromise function should have a default timeout of 5000ms.
  • All functions must be written in TypeScript.
  • The solution should be compatible with Jest 27 or higher and @testing-library/react v14 or higher.

Notes

  • Consider using libraries like lodash or fast-deep-equal for the deep equality assertion, but ensure you understand their behavior and limitations. Implementing your own is also acceptable.
  • Think about how to handle errors gracefully in the waitForPromise function.
  • The renderComponent function assumes you have @testing-library/react installed and configured.
  • Focus on creating a clean, well-documented, and reusable utility file. The goal is to reduce code duplication in your tests.
  • Remember to export all functions from test-utils.ts so they can be imported into your test files.
Loading editor...
typescript