Jest: Partial matching for mock assertions

Created on 20 Nov 2016  路  10Comments  路  Source: facebook/jest

Do you want to request a feature or report a bug?
Feature

What is the current behavior?
Asserting on parts of calls to mocks requires inspecting arguments or using a jasmine API

What is the expected behavior?
Asserting on parts of calls to mocks has a first-class API

Related - https://github.com/facebook/jest/pull/1843

Currently, asserting that a mock was called with a complex object is less than ideal because you have to either supply the entire matching object, use jasmine.objectContaining, or inspect the calls yourself with .mock.calls[x][y]. I'd like to propose an API like the following.

const mock = jest.fn();
mock({
    one: 'very long string',
    two: 2,
    three: '',
    four: {
        x: '42'
    }
});

expect(mock).toHaveBeenCalledWithMatch({
    one: 'very long string',
    two: 2
});

expect(mock).toHaveBeenCalledWithMatch({
    two: jest.any(Number) //like jasmine.any(Class)
});

Ideally we could go even further and add some meta-matchers to the jest object (inspired by sinon's matchers) to unlock some powerful assertions.

expect(mock).toHaveBeenCalledWithMatch({
    one: jest.match(value => value.startsWith('very')),
    two: jest.match.number(), //instead of jest.any(Number)
    three: jest.match.falsy(),
    four: jest.match.has('x')
});
New API proposal Question

Most helpful comment

Disregard my previous comments.

Nevertheless I'm not convinced we need another type of matchers, because it's all possible through asymmetric matchers (now ported to Jest) without much more typing.

e.g. you can use

expect(mock).toHaveBeenCalledWith(expect.objectContaining({
  two: expect.any(Number)
}));

All 10 comments

Have you tried using asymmetric matchers? They are exposed through expect. http://facebook.github.io/jest/docs/api.html#asymmetric-jest-matchers

Worth noting they're available since Jest 18.
And will be provided with pretty-printing soon

Disregard my previous comments.

Nevertheless I'm not convinced we need another type of matchers, because it's all possible through asymmetric matchers (now ported to Jest) without much more typing.

e.g. you can use

expect(mock).toHaveBeenCalledWith(expect.objectContaining({
  two: expect.any(Number)
}));

yep, agree with @thymikee here.

@thymikee @cpojer that's a shame. Doing something like this is not as simple with jest matchers alone:

expect(mock).toHaveBeenCalledWith(expect.objectContaining({
    value: sinon.match(v => v >= 100 && v < 200)
});

There are times when .any() just doesn't cut it.

Thanks @thymikee! This solved my problem! 馃槃

@andyearnshaw your example is the first thing which came to my mind when I read the solution provided by @thymikee.

I currently have an object which I expect to be passed to my mocked method. It's not the same instance, though. It just represents the same value and has the same internal state. A method like sinon.match would be super handy for this use case.

You can add custom asymmetric matchers today (toBeWithinRange in the docs: https://jestjs.io/docs/en/expect#expectextendmatchers). expect.match or expect.predicate sounds pretty cool.

@thymikee you just saved me after countless hours of coding. Thanks. Your solution worked for me.

@thymikee You made my day

Was this page helpful?
0 / 5 - 0 ratings