Jest: Enable jest.addCustomEqualityTester() like Jasmine has

Created on 20 Sep 2016  Â·  9Comments  Â·  Source: facebook/jest

Do you want to request a _feature_ or report a _bug_?
Request a feature.
What is the current behavior?

jest.addCustomEqualityTester() throws TypeError: jest.addCustomEqualityTester is not a function

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal repository on GitHub that we can npm install and npm test.

What is the expected behavior?

Should instead override .toEqual() with a custom matcher. In our case, this is the matcher we want:

  jest.addCustomEqualityTester(function(first, second) {
    try {
      return first.equals(second);
    }
    catch(e) {
      return undefined;
    }
  });

Run Jest again with --debug and provide the full configuration it prints. Please mention your node and npm version and operating system.

Using latest version of create-react-app, 6.5.0 of Node, Mac OS.

Most helpful comment

I know this is closed already, but it would still be very useful to be able to provide our own equality testers. In the example given above it is easy to write your own, but it becomes less easy when you're dealing with mixed types and you only want to provide custom equality for a subset of the types.

To build further on the previous example, suppose that we want to write a .toEqual function that works exactly the same as the build in .toEqual, except for objects that have a .equals function, in which case that should be used. E.g.:

expect([ 1, 2, { equals(other) { return true }]).toEqual([1,2,3]);

We can't delegate to toEqual, since we want toEqual to use our custom equality function for nested objects. The only way to implement this is to re-implement the entire toEqual function.

The underlying equals method already supports custom testers, which is exactly what we need to make this happen. It would be great if that would also be exposed to userland code.

All 9 comments

@dmitriiabramov we'll need this API in jest-matchers somehow. It's pretty useful.

i think the better way to do it would be to add a separate matcher that does custom equality without extending the global one. so, adding things instead of modifying, but we can think about other solutions to, even if it means replicating jasmine functionality

Open to all solutions. I would think that allowing someone to add #equals to a class… and .toEqual checking for that would be an appropriate default functionality of .toEqual. It honestly surprises me that Jasmine’s and Jests's .toEqual does not do this by default. My case is a common use case… but if there is a better way to do it, that’s fine.

Right now, we’re going through our whole code base to find the now-failing ones and replace them with expect(banana.equals(otherBanana)).toBe(true). But that’s hardly an ideal use of time, and, gives the opaque Expected false to be true failure. We could also replace the tests with expect(banana).toCustomEqual(otherBanana), using jest.addmatchers({}). But again, hardly an ideal use of time for bringing over a project coming from a Jasmine background…

We spent a lot of time trying to fiddle with Jest and get it to behave (e.g., setupTestFrameworkScriptFile, setupTests.js) before finally giving up and choosing from among the above-mentioned less-than-ideal options.

I will say that .toEqual has much prettier output in Jest than in Jasmine. Props!

On Sep 21, 2016, at 12:11 PM, Dmitrii Abramov [email protected] wrote:

i think the better way to do it would be to add a separate matcher that does custom equality without extending the global one. so, adding things instead of modifying, but we can think about other solutions to, even if it means replicating jasmine functionality

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub https://github.com/facebook/jest/issues/1751#issuecomment-248661743, or mute the thread https://github.com/notifications/unsubscribe-auth/AMQpjPl19SfV8y2_Z1WSkA4heJW9yOu-ks5qsVdCgaJpZM4KB9rE.

@mandysimon88 is that a third-party library that provides .equals method on its classes or a custom code?

my worry here is this:
we provide error messages with detailed diffs that show what exactly is not matching, and this diff is based on our diffing algorithm. If we allow extending toEqual by using custom methods, we won't be able to guarantee that error messages well make sense.

What i think is the best solution in your case is to use .toCustomEqual and codemod the existing usages of .toEqual to the new .toCustomEqual matcher. I'll try to look at writing a codemod that can modify only failing tests after a jest update.

thanks for your feedback and sorry, our matcher system is not that flexible yet, but we're working on it :)

No, .equals is something we add to our classes when needed, not a third-party library. For example:

class Point {
  constructor(x,y) {
     this.x = x
     this.y = y
  }
  equals(anotherPoint) {
     return this.x === anotherPoint.x && this.y === anotherPoint.y
  }
}

I see your point, that if you allowed .toEqual to be extended by adding an equality tester, that .toEqual’s error messages would make less sense. I’m realizing that Jest has gotten away from Jasmine in a lot of ways (probably via improvements in most cases). It was perhaps naive of us to come in thinking we could make the transition with an existing codebase as smoothly as we’d hoped. The codemod you describe would be really helpful.

On Sep 21, 2016, at 1:46 PM, Dmitrii Abramov [email protected] wrote:

@mandysimon88 https://github.com/mandysimon88 is that a third-party library that provides .equals method on its classes or a custom code?

my worry here is this:
we provide error messages with detailed diffs that show what exactly is not matching, and this diff is based on our diffing algorithm. If we allow extending toEqual by using custom methods, we won't be able to guarantee that error messages well make sense.

What i think is the best solution in your case is to use .toCustomEqual and codemod the existing usages of .toEqual to the new .toCustomEqual matcher. I'll try to look at writing a codemod that can modify only failing tests after a jest update.

thanks for your feedback and sorry, our matcher system is not that flexible yet, but we're working on it :)

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/facebook/jest/issues/1751#issuecomment-248688692, or mute the thread https://github.com/notifications/unsubscribe-auth/AMQpjJA6ZRC0PQRNdTCk3ks1JPpVifrWks5qsW2DgaJpZM4KB9rE.

+1, math work needs floating point based equality tests.

@mandysimon88 @guy-kdm you can use expect.extend to achieve that.
Example implementation:

expect.extend({
  toCustomEqual(received, expected) {
    let pass = false;
    let message;

    try {
      pass = received.equals(expected);
    } catch (error) {
      message = `${received}.equals() is not a function`
    }

    message = message ||
      this.utils.matcherHint('.toCustomEqual') + '\n\n' +
        `Expected value to not be (using .equals()):\n` +
        `  ${this.utils.printExpected(expected)}\n` +
        `Received:\n` +
        `  ${this.utils.printReceived(received)}`;

    return {actual: received, message, pass};
  }
})

Closing this for now, but happy to reopen if necessary.

I know this is closed already, but it would still be very useful to be able to provide our own equality testers. In the example given above it is easy to write your own, but it becomes less easy when you're dealing with mixed types and you only want to provide custom equality for a subset of the types.

To build further on the previous example, suppose that we want to write a .toEqual function that works exactly the same as the build in .toEqual, except for objects that have a .equals function, in which case that should be used. E.g.:

expect([ 1, 2, { equals(other) { return true }]).toEqual([1,2,3]);

We can't delegate to toEqual, since we want toEqual to use our custom equality function for nested objects. The only way to implement this is to re-implement the entire toEqual function.

The underlying equals method already supports custom testers, which is exactly what we need to make this happen. It would be great if that would also be exposed to userland code.

@TiddoLangerak 's last point about recursive testing is what convinced me custom matcher is a poor way to handle this. Did you end up finding a workaround in the last few years?

Was this page helpful?
0 / 5 - 0 ratings