Jest matchers for projects using fp-ts
and io-ts
.
If your TypeScript project is written in a functional programming style using fp-ts
and io-ts
, many
of the values you'll want to check in your unit tests will come wrapped inside container types like
Either, Option, or These. Jest has no awareness of these container types and no built-in matchers to
help you to compare wrapped values against un-wrapped values. This leaves you with two options:
- Extract the received value from the container type before using a jest matcher.
- Lift the expected value into a container of the expected type before using a jest matcher.
Both options work, but tend to make your tests somewhat verbose, adding unnecessary work when writing your tests, and making it harder to read and maintain them.
@relmify/jest-fp-ts
aims to add additional matchers to Jest's default ones, making it easier to test code
that makes use of fp-ts
functional containers.
With npm:
npm install -D @relmify/jest-fp-ts
With yarn:
yarn add -D @relmify/jest-fp-ts
You also need both fp-ts
and io-ts
installed in your project. If you're
here, presumably you're already using fp-ts
. Not every fp-ts
project uses
io-ts
though. If you aren't using io-ts
in your project, simply install it as
a development dependency.
With npm:
npm install -D io-ts
With yarn:
yarn add -D io-ts
Add @relmify/jest-fp-ts
to your Jest setupFilesAfterEnv
configuration.
See jest documentation for additional help.
In your package.json
file add:
"jest": {
"setupFilesAfterEnv": ["@relmify/jest-fp-ts"]
}
Instead of configuring jest using package.json
, you can use a jest.config.js
file to specify the setupFilesAfterEnv
configuration. There you can specify a setup file such as ./jest.setup.js
that will require @relmify/jest-fp-ts
:
// jest.config.js
module.exports = {
setupFilesAfterEnv: ['./jest.setup.js'],
};
// jest.setup.js
require('@relmify/jest-fp-ts');
If you are using other jest custom matcher packages too, you can require
them in the same jest.setup.js
file.
If your editor does not recognise the custom @relmify/jest-fp-ts
matchers, add a global.d.ts
file to your project with:
import '@relmify/jest-fp-ts';
- Problem
- Solution
- Installation
- Setup
- Matchers
- Asymmetric matchers
- LICENSE
- Contributing
Use .toBeEither()
to check if a value is an Either (either a Left or a Right).
Use .toBeLeft()
to check if a value is a Left.
Use .toBeLeftWithErrorsMatching(value)
when testing errors returned from io-ts codec decode()
operations.
An io-ts type codec decode
method will return a left
with an array of ValidationError objects if
the supplied value does can not be successfully validated and decoded to the specified io-ts type.
For codecs that are composed from multiple codecs, multiple errors may be returned as each sub-codec is applied to the values it is charged with validating.
This matcher provides an easy way to check if expected validation errors are present. To do this, it makes use of the io-ts PathReporter module.
To use this matcher, supply an array of strings that you expect to be present in the array of
strings returned by PathReporter.report()
. You can supply either regular expressions or
substrings. The matcher will try to match each array entry against the array of ValidationErrors.
If the supplied object is not a Left that contains an array of ValidationErrors, or if any of the
strings you supply cannot be matched to one of the ValidationErrors, the matcher will return
false
. If all of the strings you supply are matched, it will return true
.
const Name = t.type({
first: t.string,
last: t.string,
});
type Name = t.TypeOf<typeof Name>;
const numberName = { first: 404, last: 401 };
test('if the received is a Left that contains errors matching the expected values', () => {
expect(Name.decode(numberName)).toBeLeftWithErrorsMatching([/404/, '401']);
});
Use .toBeRight()
to check if a value is a Right.
Use .toEqualLeft(value)
to check if a value is a Left that contains a value that equals an
expected value. See Jest's toEqual(value)
documentationfor information about how the .toEqual()
comparison works.
Use .toEqualRight(value)
to check if a value is a Right that contains a value that equals an
expected value. See Jest's toEqual(value)
documentationfor information about how the .toEqual()
comparison works.
Use .toStrictEqualLeft(value)
to check if a value is a Left that contains a value that strictly
equals an expected value. See Jest's
toStrictEqual(value) documentation for
information about how .toStrictEqual()
differs from toEqual()
.
Use .toStrictEqualRight(value)
to check if a value is a Right that contains a value that
strictly equals an expected value. See Jest's
toStrictEqual(value) documentation for
information about how .toStrictEqual()
differs from toEqual()
.
Use .toSubsetEqualLeft(value)
to check if a value is a Left that contains an object with a
subset of properties that match the expected object properties. The received value must contain all
of the expected properties, and may contain more than the expected properties.
Use .toSubsetEqualRight(value)
to check if a value is a Right that contains an object with a
subset of properties that match the expected object properties. The received value must contain all
of the expected properties, and may contain more than the expected properties.
Use .toBeNone()
to check if a value is a None.
Use .toBeOption()
to check if a value is a Option (either a Some or a None).
Use .toBeSome()
to check if a value is a Some.
Use .toEqualSome(value)
to check if a value is a Some that contains a value that equals an
expected value. See Jest's toEqual(value)
documentationfor information about how the .toEqual()
comparison works.
Use .toStrictEqualSome(value)
to check if a value is a Some that contains a value that
strictly equals an expected value. See Jest's
toStrictEqual(value) documentation for
information about how .toStrictEqual()
differs from toEqual()
.
Use .toSubsetEqualSome(value)
to check if a value is a Some that contains an object with a
subset of properties that match the expected object properties. The received value must contain all
of the expected properties, and may contain more than the expected properties.
All of the provided matchers are asymmetric matchers, which means that they can be called from
within a standard jest .toEqual(value)
like so:
test('passes if called as an asymmetric matcher', () => {
expect(left('People who are truly strong lift others up.')).toEqual(
expect.toEqualLeft('People who are truly strong lift others up.'),
);
});
The provided toEqual*(value)
matchers also accept asymmetric matchers - just like Jest's
toEqual(value)
does - which means you can pass in any of the standard Jest asymmetric matchers, or any
of the jest-extended matchers.
test('if called with an asymmetric matcher', () => {
expect(left('People who are truly powerful bring others together.')).toEqualLeft(
expect.stringContaining('powerful'),
);
});
If you've come here to help contribute - Thanks! Take a look at contributing to see how to get started.