Hone logo
Hone
Problems

Building Robust Test Utilities with Jest in TypeScript

Writing maintainable and readable tests is crucial for software quality. This challenge focuses on creating reusable test utilities in Jest using TypeScript, allowing you to simplify your test code and reduce duplication. You'll build functions to handle common testing tasks, making your tests more efficient and easier to understand.

Problem Description

You are tasked with creating a set of utility functions to enhance your Jest testing suite. These utilities should encapsulate common testing patterns, such as asserting against specific data structures, mocking functions, and handling asynchronous operations. The goal is to create a testUtils.ts file containing these utilities, which can then be imported and used in your component or module tests.

Key Requirements:

  1. mockImplementation Helper: Create a function mockImplementationFactory that takes a function as input and returns a mock function with the provided implementation. This simplifies mocking functions with custom logic.
  2. Deep Equality Assertion: Create a function deepEquals that takes two objects as input and returns true if they are deeply equal (meaning all properties and their values are the same), and false otherwise. Use JSON.stringify for a simple deep comparison.
  3. Async Wait Helper: Create an asynchronous function waitForAsync that takes a promise as input and returns a promise that resolves when the input promise resolves. This is useful for waiting for asynchronous operations to complete within your tests.
  4. Error Message Helper: Create a function errorMessage that takes a string and returns a more descriptive error message suitable for Jest assertions. Prefix the input string with "Assertion failed: ".

Expected Behavior:

  • mockImplementationFactory should return a Jest mock function with the specified implementation.
  • deepEquals should correctly compare objects for deep equality.
  • waitForAsync should wait for the provided promise to resolve before resolving itself.
  • errorMessage should prepend "Assertion failed: " to the input string.

Edge Cases to Consider:

  • deepEquals: Consider cases where objects have circular references (though a simple JSON.stringify approach will likely fail in these cases, it's good to be aware). For this challenge, circular references are not a primary concern.
  • waitForAsync: Handle potential promise rejections gracefully. The function should reject with the same error as the input promise.
  • Empty objects and arrays in deepEquals.

Examples

Example 1:

Input: mockImplementationFactory(() => 'mocked')
Output: A Jest mock function that, when called, returns 'mocked'.
Explanation: The factory creates a mock function with the provided implementation.

Example 2:

Input: deepEquals({a: 1, b: {c: 2}}, {a: 1, b: {c: 2}})
Output: true
Explanation: Both objects have the same properties and values.

Input: deepEquals({a: 1, b: {c: 2}}, {a: 1, b: {c: 3}})
Output: false
Explanation: The value of 'c' differs between the two objects.

Example 3:

Input: waitForAsync(Promise.resolve(5))
Output: A promise that resolves to 5 after the input promise resolves.
Explanation: waitForAsync waits for the input promise to resolve.

Input: waitForAsync(Promise.reject(new Error('Test failed')))
Output: A promise that rejects with the same error ('Test failed').
Explanation: waitForAsync propagates the rejection of the input promise.

Constraints

  • The solution must be written in TypeScript.
  • The solution must use Jest's mocking capabilities.
  • The deepEquals function should use JSON.stringify for simplicity. More robust deep comparison libraries exist, but are not required for this challenge.
  • The waitForAsync function should not introduce any unnecessary delays.
  • All functions must be exported from a single testUtils.ts file.

Notes

  • Consider using Jest's jest.fn() to create mock functions.
  • The waitForAsync function is useful for testing asynchronous components or functions.
  • The deepEquals function can be extended to handle more complex data structures or custom comparison logic if needed.
  • Focus on creating clean, readable, and reusable utility functions. Good naming conventions are important.
  • Think about how these utilities can simplify your existing or future tests.
Loading editor...
typescript