Jest · Unit tests that survive refactors
How do you write custom Jest matchers?
Short answer
Register a matcher with expect.extend, returning pass and a message. Custom matchers remove repeated assertion logic and produce domain-specific failure messages.
A custom matcher is useful when the same multi-step assertion appears across many tests. It centralises the comparison and, more importantly, the failure message. A good matcher explains what was expected in the domain's language.
expect.extend({
toBeWithinRange(received: number, floor: number, ceiling: number) {
const pass = received >= floor && received <= ceiling;
return {
pass,
message: () =>
"expected " + received + " to be within " + floor + " and " + ceiling,
};
},
});
test("stays in range", () => {
expect(7).toBeWithinRange(1, 10);
});Writing a good matcher
- Return an object with
passand amessagefunction - Use
this.isNotto give a different message when negated - Include
this.utils.printExpectedandprintReceivedfor readable output - Put registration in
setupFilesAfterEnvso every test sees it - Add a TypeScript declaration so the matcher is typed
Keep matchers generic. A matcher named toBeValidInvoice that checks ten unrelated rules is hard to diagnose when it fails. Prefer several small matchers with clear names, and let the test name describe the scenario.
toBe(true), it hides the value and produces a worse failure message than just asserting on the value directly.Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1How do you type a custom matcher for TypeScript?
- 2Why should the message be a function rather than a string?
- 3How do you handle negation correctly in a matcher?
- 4When is a helper function better than a matcher?
Say it in the interview
I register a matcher with expect.extend and return pass plus a message function. The message needs to explain the expected and received values in domain terms, and it has to respect this.isNot for negated assertions. I put registration in setupFilesAfterEnv and add a type declaration. I only create one when the same assertion repeats and benefits from a better failure message.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Jest mock interview and get scored feedback on depth, clarity and confidence.