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
- yarn
- bun
npm install -D mix-n-matchers
yarn add -D mix-n-matchers
bun add -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.
- Jest
- Jest (no globals)
- Vitest
import "mix-n-matchers/all";
import type { Config } from 'jest';
const config: Config = {
setupFilesAfterEnv: ["<rootDir>/test-setup.ts"]
};
export default config;
import "mix-n-matchers/jest-globals";
import type { Config } from 'jest';
const config: Config = {
injectGlobals: false,
setupFilesAfterEnv: ["<rootDir>/test-setup.ts"]
};
export default config;
import "mix-n-matchers/vitest";
export default defineConfig({
test: {
setupFiles: ["./test-setup.ts"],
},
})
Manual setup
If you'd prefer to add the matchers manually, you can import the individual matchers as needed into your setup file.
- Jest
- Jest (no globals)
- Vitest
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> {}
}
}
import { expect } from "@jest/globals";
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 module "@jest/expect" {
interface Matchers<R, T> extends MixNMatchersFrom<typeof matchers, R, T> {}
interface AsymmetricMatchers
extends AsymmetricMixNMatchersFrom<typeof matchers> {}
}
import { expect } from "vitest";
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 module "vitest" {
interface Assertion<T> extends MixNMatchersFrom<typeof matchers, void, T> {}
interface AsymmetricMatchersContaining
extends AsymmetricMixNMatchersFrom<typeof 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.
- Jest
- Jest (no globals)
- Vitest
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"];
}
}
}
import { expect } from "@jest/globals";
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 module "@jest/expect" {
interface Matchers<R, T> extends MixNMatchersFrom<typeof matchers, R, T> {
toBeTypeOf: AsymmetricMixNMatchers["typeOf"];
}
interface AsymmetricMatchers extends AsymmetricMixNMatchersFrom<typeof matchers> {
calledWithContext: MixNMatchers<void, any>["toBeCalledWithContext"];
}
}
import { expect } from "vitest";
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 module "vitest" {
interface Assertion<T> extends MixNMatchersFrom<typeof matchers, void, T> {
toBeTypeOf: AsymmetricMixNMatchers["typeOf"];
}
interface AsymmetricMatchersContaining
extends AsymmetricMixNMatchersFrom<typeof matchers> {
calledWithContext: MixNMatchers<void, any>["toBeCalledWithContext"];
}
}