Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Use jest-diff in toHaveBeenCalledWith matcher results #676

Open
olivierlacan opened this issue Feb 7, 2024 · 0 comments
Open

Use jest-diff in toHaveBeenCalledWith matcher results #676

olivierlacan opened this issue Feb 7, 2024 · 0 comments

Comments

@olivierlacan
Copy link

olivierlacan commented Feb 7, 2024

Feature Request

Description

toHaveBeenCalledWith is a very helpful matcher, its output less so.

Jest users shouldn't have to scan JS objects to try and figure out what specific property happens to differ.

Possible solution

Here's a (very) naive implementation of this feature as toHaveBeenCalledWithDiff which wraps toHaveBeenCalledWith and simply runs jest-diff on expected & actual:

// add this to a file listed in `setupFilesAfterEnv` in `jest.config.ts`,
// for example `jest.setup.ts`: 

import { diff } from "jest-diff";

expect.extend({
  toHaveBeenCalledWithDiff(received, ...expected) {
    // Check if the function was called
    if (received.mock.calls.length === 0) {
      return {
        message: () => `expected mock function to be called but it was not`,
        pass: false,
      };
    }

    // Loop through all calls to find a match
    const calls = received.mock.calls;
    for (let i = 0; i < calls.length; i++) {
      const actual = calls[i];
      const pass = this.equals(actual, expected);

      if (pass) {
        return {
          message: () => `all good`,
          pass: true, // The actual and expected arguments match
        };
      } else {
        // Generate and log the diff if the arguments do not match
        const diffString = diff(expected, actual, {
          expand: this.expand,
        });

        return {
          message: () => `expected mock function to be called with:\n${this.utils.printExpected(expected)}\nbut it was called with:\n${this.utils.printReceived(actual)}\n\nDifference:\n\n${diffString}`,
          pass: false, // The actual and expected arguments do not match
        };
      }
    }

    // If none of the calls match the expected arguments
    return {
      message: () => `expected mock function to be called with ${this.utils.printExpected(expected)} but it was not`,
      pass: false,
    };
  }
}); 

Motivation

Given the following expectation:

expect(mockService.createOrUpdate).toHaveBeenCalledWithDiff(
  expect.objectContaining({
    id: expect.any(String),
    authorIds: [],
    description: mock.description,
    longDescription: mock.longDescription,
    educationLevel: EducationLevelType.BEGINNER,
    imageUrl: expect.any(String),
    opportunityId: mock.opportunityId,
    firstPublishedAt: expect.any(Date),
    retiredAt: undefined,
    type: "video",
    title: mock.title,
  }),
);

Is this really useful output? Or could Jest as a testing tool actually help programmers focus on the differences that matter?

expected mock function to be called with:
[ObjectContaining {"authorIds": [], "description": "Deficio aetas eum capitulus a.", "educationLevel": "BEGINNER", "firstPublishedAt": Any<Date>, "id": Any<String>, "imageUrl": Any<String>, "longDescription": "Tersus adicio varius quo bardus ulterius speculum deprimo temptatio. Angulus surgo tondeo. Tandem vita decretum adulescens corpus advenio deleo adhuc cruciamentum.", "retiredAt": undefined, "title": "curo totam cupressus", "type": "video"}]
but it was called with:
[{"authorIds": [], "description": "Deficio aetas eum capitulus a.", "educationLevel": "BEGINNER", "firstPublishedAt": 2024-02-06T03:20:04.037Z, "id": undefined, "imageUrl": "https://picsum.photos/seed/DFLZ75/640/480", "longDescription": "Tersus adicio varius quo bardus ulterius speculum deprimo temptatio. Angulus surgo tondeo. Tandem vita decretum adulescens corpus advenio deleo adhuc cruciamentum.", "retiredAt": undefined, "title": "curo totam cupressus", "type": "video"}]

Can you. tell me what's causing the test to fail here? Should I expect you to run a manual diff on this output when jest-diff is already used within Jest itself to provide similar diffs?

Outcome

Wouldn't toHaveBeenCalledWith be a much more valuable tool if it provided this output instead?

- Expected
+ Received

@@ -1,15 +1,15 @@
  Array [
-   ObjectContaining {
+   Object {
      "authorIds": Array [],
      "description": "Deficio aetas eum capitulus a.",
      "educationLevel": "BEGINNER",
-     "firstPublishedAt": Any<Date>,
-     "id": Any<String>,
-     "imageUrl": Any<String>,
+     "firstPublishedAt": 2024-02-06T03:20:04.037Z,
+     "id": undefined,
+     "imageUrl": "https://picsum.photos/seed/DFLZ75/640/480",
      "longDescription": "Tersus adicio varius quo bardus ulterius speculum deprimo temptatio. Angulus surgo tondeo. Tandem vita decretum adulescens corpus advenio deleo adhuc cruciamentum.",
      "opportunityId": "bef7fbc0-e964-4770-95b8-9a6667effac4",
      "retiredAt": undefined,
      "title": "curo totam cupressus",

Granted, it does highlight type expectations as differences which can be a bit confusing, but it does help narrow the search far more quickly: id was expected to be Any<String> but it's undefined.

All done, ready to fix the test now. 😄

PS: I checked toContain* matchers in jest-extended and was surprised they don't seem to offer this either. But it's possible I've missed something.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant