Open georeith opened 4 years ago
I ended up here from #2547, and @SimenB you'd asked for use cases in that one (...admittedly ~3 years ago :-D), but similar to @georeith I want to make a custom matcher that a) accepts args, b) does some pre-processing, and then c) hands off to an existing matcher, in my case toMatchObject
to leverage it's great out-of-the-box formatting/diffing/etc capabilities.
Basically, in our project, the actual
instance that is passed to my expect(actual).toMatchObject({ ... })
has ugly implementation details that I want to clean up (almost like a .toJSON
to get it to be "just data") for the toMatchObject
.
In my case I'm using a require
hack for now:
export async function toMatchEntity<T>(actual: Entity, expected: MatchedEntity<T>): Promise<CustomMatcherResult> {
// Clean up `actual` to be "just data"
const copy = ...project specific stuff...
// Blatantly grab `toMatchObject` from the guts of expect
const { getMatchers } = require("expect/build/jestMatchersObject");
// Now use `toMatchObject` but with our "just data" version of `actual`
return getMatchers().toMatchObject.call(this, copy, expected);
}
With @georeith 's proposal, the require
hack would go away and this could become:
return expect.extend({
toMatchEntity(actual, expected) {
const copy = ...same clean up...;
return this.matchers.toMatchObject(copy, expected);
},
});
@stephenh This works great, I have been using a similar code for some time now and it's šÆ . However recently I have tried to do the same with expect.objectContaining
have you had any success doing the same with these asymmetric matchers?
@bpinto hm, no, I haven't tried to re-use objectContaining
yet, so I'm not sure how/if it would be different.
Another vote for the core matchers to be exposed for use within custom matchers.
In my use case I'd like to write a db-based custom matcher expect(original).toHaveBeenUpdatedTo({ā¦})
.
Internally this would be
import { toMatchObject } from 'somewhere'
export const toHaveBeenUpdatedTo = async (original, match) => {
const updated = await getUpdatedFromDatabase(original)
return toMatchObject(updated, match)
}
that would be extremely helpful to be able to re-use existing matchers in custom matchers.
This no longer works, and breaks with the error message:
Cannot find module 'expect/build/jestMatchersObject' from '__tests__/extend-expect.ts'
See this PR: https://github.com/facebook/jest/pull/13375
This should be merged then ;)
@mrazauskas until the PR is merged, I'd like to understand why the import does not work anymore. The file is there, why can't it be resolved?
Perhaps newer version of Node is taking into account exports
while resolving paths?
I also need this, I found these hacks to be working
const { matchers } = globalThis[Symbol.for('$$jest-matchers-object')];
// or
import matchers from 'expect/build/matchers';
// or
const matchers = require('expect/build/matchers').default;
Ah wow, I'm trying out Jest v30.0.0-alpha.2 and the import from expect/build/matchers
doesn't work anymore, but @davispuh 's globalThis[$$jest-matchers-object]
does work! In both Jest v29 and Jest v30 :tada: . Thanks @davispuh !
š Feature Proposal
Expose existing matchers inside
expect.extend
.Motivation
Sometimes you want the existing functionality of a matcher but you want to it to transform the input before doing so, for instance, to ignore some specific keys of an object.
Writing a custom matcher is extremely verbose and requires importing additional packages to maintain the same quality of the core matchers (diff in messages).
For example, if I want a matcher that performs
toEqual
on two objects but ignores a single property on those objects:Example
and then:
Pitch
I am aware this has been asked for before:
The response was to use
expect.extend
and I do not think it considers these cases where usingexpect.extend
as it stands is not only massively inconvenient upfront for such a simple comparison but creates longer term debt having to maintain the matcher, whereas leveraging the return value of a core matcher allows your matcher to benefit from the continued maintenance of it in the jest core, e.g., if it gets improved messages or the already very verbose matcher return API changes.This proposal is to enable the ability to write matchers that don't want to introduce new matching behaviour but want to transform their inputs before matching.
Other alternatives include:
You then have to handle
not
yourself by either making separate functions or flagging it:Which will work, but now requires you to know an entirely different syntax because of a slight difference to the matcher.