Hone logo
Hone
Problems

Implement a Custom Jest Matcher: toMatchObject

Jest is a popular JavaScript testing framework that provides a rich set of built-in matchers for assertions. One such matcher is toMatchObject, which allows you to assert that an object contains a subset of properties and their values. This challenge asks you to implement a custom version of toMatchObject in TypeScript for Jest.

Problem Description

You need to create a Jest custom matcher named toMatchObject that checks if a received object contains all the properties and their corresponding values specified in the expected object. This is useful for testing components or functions that return complex objects, where you might only care about a specific subset of the returned data.

Key Requirements:

  1. The matcher should accept an expected object as an argument.
  2. It should compare the received object (the actual object being tested) against the expected object.
  3. The matcher should pass if, for every key-value pair in expected, the received object has the same key and the same value.
  4. The matcher should fail if any key from expected is missing in received, or if the value associated with a key in expected does not match the value in received.
  5. Nested objects should also be compared recursively. If a property in expected is an object, the corresponding property in received must also be an object and contain all the properties of the nested expected object.
  6. Arrays are compared by reference. If an array is expected, the received value must be the exact same array instance. This mimics Jest's default behavior for toMatchObject.

Expected Behavior:

  • If received contains all the properties and values of expected (including nested object comparisons), the matcher should report success.
  • If received is missing any properties from expected, or if any values do not match, the matcher should report failure with a clear message indicating what failed (e.g., missing key, mismatched value, mismatched nested object).

Edge Cases to Consider:

  • expected object is empty: Should always pass.
  • received object is empty: Should pass only if expected is also empty.
  • received object has extra properties not present in expected: This should not cause the matcher to fail; only properties in expected need to be present and match.
  • null or undefined values in expected or received.
  • Non-object types in expected or received (e.g., primitives, arrays, null).

Examples

Example 1: Basic Object Matching

Input: received: { name: 'Alice', age: 30, city: 'New York' } expected: { name: 'Alice', age: 30 }

Output: Pass

Explanation: The received object contains both name and age with matching values. The extra city property in received is ignored.

Example 2: Mismatched Value

Input: received: { name: 'Alice', age: 30 } expected: { name: 'Alice', age: 25 }

Output: Fail

Explanation: The age property in expected is 25, but in received it is 30.

Example 3: Missing Property

Input: received: { name: 'Alice' } expected: { name: 'Alice', age: 30 }

Output: Fail

Explanation: The age property is missing from the received object.

Example 4: Nested Object Matching

Input: received: { user: { id: 1, name: 'Bob', address: { street: '123 Main St', zip: '10001' } } } expected: { user: { name: 'Bob', address: { street: '123 Main St' } } }

Output: Pass

Explanation: The user object in received contains a name matching expected, and its nested address object contains a street matching expected. The id in received.user and zip in received.user.address are ignored.

Example 5: Mismatched Nested Object

Input: received: { user: { id: 1, name: 'Bob', address: { street: '123 Main St', zip: '10001' } } } expected: { user: { name: 'Bob', address: { street: '456 Oak Ave' } } }

Output: Fail

Explanation: The street property within the nested address object does not match.

Example 6: Array Comparison (Reference Equality)

Input: received: { items: [1, 2, 3] } expected: { items: [1, 2, 3] }

Output: Fail

Explanation: Jest's toMatchObject for arrays checks for reference equality. Since these are two different array instances, the matcher fails. If expected was received.items, it would pass.

Constraints

  • The implementation should be written in TypeScript.
  • The custom matcher should be compatible with Jest's custom matcher API.
  • The comparison should handle up to 10 levels of nested objects.
  • The number of properties in an object can be up to 100.

Notes

  • You'll need to define a Jest custom matcher. This typically involves extending jest.Expect.
  • Consider how you will recursively traverse the expected and received objects.
  • Think about how to generate informative failure messages.
  • Remember that received might not be an object or might be null or undefined. Your matcher should handle these cases gracefully.
  • The behavior with arrays is crucial to get right; it's reference equality, not deep equality.
Loading editor...
typescript