Skip to main content

Setup

Mix 'n' Matchers is a set of custom Jest matchers, aiming to fill perceived gaps in the Jest matcher ecosystem. This guide will help you get started.

Installation

Install with your package manager of choice:


npm install -D mix-n-matchers

Configuration

Automatic setup

The easiest way to add the matchers to your environment is by importing one of the automatic setup files, which will add all of the matchers for you.

Import one of these into a setup file, and include that setup file in your Jest/Vitest configuration. Using this setup will also ensure that the matchers are available in your TypeScript files.

test-setup.ts

import "mix-n-matchers/all";

jest.config.ts

import type { Config } from 'jest';

const config: Config = {
setupFilesAfterEnv: ["<rootDir>/test-setup.ts"]
};

export default config;

Manual setup

If you'd prefer to add the matchers manually, you can import the individual matchers as needed into your setup file.

test-setup.ts
import {
toBeCalledWithContext,
lastCalledWithContext,
nthCalledWithContext,
exactly,
} from "mix-n-matchers";
import type { MixNMatchersFrom, AsymmetricMixNMatchersFrom } from "mix-n-matchers";

const matchers = {
toBeCalledWithContext,
lastCalledWithContext,
nthCalledWithContext,
exactly,
};

expect.extend(matchers);

declare global {
namespace jest {
interface Matchers<R, T> extends MixNMatchersFrom<typeof matchers, R, T> {}

interface Expect extends AsymmetricMixNMatchersFrom<typeof matchers> {}

interface InverseAsymmetricMatchers
extends AsymmetricMixNMatchersFrom<typeof matchers> {}
}
}

Asymmetric Matchers vs Symmetric Matchers

When expect.extend is called, each matcher is added as both an asymmetric and symmetric matcher.

expect.extend({
foo(received) {
const pass = received === "foo";
return {
pass,
message: pass ? () => "Expected 'foo'" : () => "Expected not 'foo'",
};
},
});

expect(value).foo(); // symmetric

expect(value).toEqual(expect.foo()); // asymmetric

However, conventionally there is a difference in how these matchers are named. For example, .toBeAnArray() vs expect.array().

mix-n-matchers intentionally only exposes types for matchers as either asymmetric or symmetric, and not both. Sometimes a matcher is available as both, but with different names. For example, .toBeEnum() and expect.ofEnum.

This helps to avoid confusion and makes it clear which matchers are designed to be asymmetric and which are symmetric.

If there's any existing matchers that are only available as asymmetric matchers and you'd like to use them as symmetric matchers (or vice versa), please open an issue or a pull request!

You can of course choose to setup the matchers as both asymmetric and symmetric matchers if you prefer.

test-setup.ts
import { typeOf, toBeCalledWithContext } from "mix-n-matchers";
import type {
MixNMatchers,
MixNMatchersFrom,
AsymmetricMixNMatchers,
AsymmetricMixNMatchersFrom
} from "mix-n-matchers";

const matchers = {
typeOf,
toBeTypeOf: typeOf,
toBeCalledWithContext,
calledWithContext: toBeCalledWithContext,
};

expect.extend(matchers);

declare global {
namespace jest {
interface Matchers<R, T> extends MixNMatchersFrom<typeof matchers, R, T> {
toBeTypeOf: AsymmetricMixNMatchers["typeOf"];
}

interface Expect extends AsymmetricMixNMatchersFrom<typeof matchers> {
calledWithContext: MixNMatchers<void, any>["toBeCalledWithContext"];
}

interface InverseAsymmetricMatchers
extends AsymmetricMixNMatchersFrom<typeof matchers> {
calledWithContext: MixNMatchers<void, any>["toBeCalledWithContext"];
}
}
}