Jest expect mock to be called with an object not containing a specific field
Asked Answered
B

4

5

I want to verify that a mocked API is called with an object which does not contain a specific field.

expect(api.method).toBeCalledWith(expectedObject);

Since the actual parameters of the api.method call can be a subset of the expectedObject this test also passes if the actualObject contains further fields (among which the specific field might be).

How can I rewrite the test in a way, that the tests fails if the actualObject is not equal to the expectedObject?

Brush answered 14/3, 2019 at 10:16 Comment(0)
M
6

you can try something like this

// get the first call to your method
const method = api.method.mock.calls[0]; 

//check if you have expectedObject as a subset of the arguments
expect(method[0]).toMatchObject(expectedObject); 

//key is the field that shouldn't be part of the arguments
expect(method[0].has(key)).toEqual(false); 
Metaphysics answered 14/3, 2019 at 10:34 Comment(1)
Thanks a lot Karim! That got me on the right track!Brush
B
3
// since several tests use this mock I have to ensure to have the latest call
const lastMockApiCall = api.method.mock.calls[api.method.mock.calls.length - 1]; 
const apiCallParams = lastMockApiCall[0];
expect(apiCallParams).not.toHaveProperty('specificFieldIdontWant');
Brush answered 14/3, 2019 at 15:0 Comment(1)
Whilst this code snippet is welcome, and may provide some help, it would be greatly improved if it included an explanation of how and why this solves the problem. Remember that you are answering the question for readers in the future, not just the person asking now! Please edit your answer to add explanation, and give an indication of what limitations and assumptions apply.Tsai
C
1

Even when the previous methods does work, it feels uncomfortable to access 0 indexes and nested properties and, in the end, doing manual comparisons. So I prefer this method:

expect(api.method).toBeCalledWith(
  expect.not.objectContaining({
    notWantedProperty: expect.anything(),
  })
);

Also, this will check for previous calls and not only the indexed call. And looks better on the logs:

    Expected: ObjectNotContaining {"name": Anything}
    Received: ...

Be careful, since it will try to match the whole object and not every property separately. For example, if your api has been called with the following object:

{
  "foo": "foo",
  "bar": "bar"
}

The following expect would NOT fail:

expect(api.method).toBeCalledWith(
  expect.not.objectContaining({
    foo: expect.anything(),
    baz: expect.anything(),
  })
);

Because you didn't call the API with both properties, so I would create a new expect for every property:

expect(api.method).toBeCalledWith(
  expect.not.objectContaining({ foo: expect.anything() })
);
expect(api.method).toBeCalledWith(
  expect.not.objectContaining({ baz: expect.anything() })
);
Curdle answered 10/7 at 12:32 Comment(0)
P
0

I'm using the following

expect(body[0].email).toEqual(undefined);

Which is passing when there is no email in the first object of the returned API array.

Poltroon answered 23/11, 2021 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.