Jest Assymetric Matcher — objectContaining

Abhinav
2 min readJan 20, 2023

--

In unit tests, there are times where we only want to match 4–5 properties of an Object, and don’t really care about the others, or can’t control them. In a typical unit testing framework, we either write 4 or 5 assertions, or create the whole expected object and match it with the actual one.

Jest makes it easy to handle these scenarios using assymetric matchers. In this blog, we’ll look at objectContaining .

Imagine the following scenario — we want to assert that we’re going to receive an error, and assert some properties in that error. The easiest way of doing that is —

let error: any;
try{
await process(errorParams);
}catch(err: any){
error = err;
}
expect(error.isBoom).toBeTruthy();
expect(error.output.statusCode).toEqual(400);
expect(error.data.status).toEqual(STATUS.FAILED);

The good thing about the approach above is that it is simple, and achieves the purpose. However it looks hacky.

To make it less hacky, we can do this —

const expectedError = <create the entire expected object here>

await expect(process(errorParams)).rejects.toThrow(expectedError);

This approach looks less hacky, but is actually quite painful. For every assertion, we have to create the entire error object, which might take a bit of effort, and sometimes might be more painful than the first approach if we’re depending on external libraries.

However, jest gives us an asymmetric matcher (a matcher that can match more than just equality) called objectContaining that just lets us assert that the actual object contains a few fields that we want.

await expect(process(errorParams)).rejects.toThrow(
expect.objectContaining({
isBoom: true,
output: expect.objectContaining({statusCode: 400}),
data: expect.objectContaining({status: STATUS.FAILED})
})
);

Here, the code exactly tells us what we’re asserting, so its easier to read. At the same time, the developer does not have to create complicated objects to assert against.

I found one downside to this approach though — debugging was quite difficult. Jest did not explicitly call out the exact mismatch between the expected and actual objects, it just called out that the assertion failed with an error that was not easy to understand. So it might make sense to do assertions with smaller objectContaining blocks. In the above example, the data assertion can be a different assertion in a different test, and this test can focus on just asserting the type and error code.

In the end, if you take care to write unit tests where everything is not asserted in a single test, it makes it easier to debug. That holds even more true for this matcher.

--

--