jest-mock-extended icon indicating copy to clipboard operation
jest-mock-extended copied to clipboard

Order of calling mock.calledWith() matters

Open derek-pavao opened this issue 4 years ago • 3 comments

Unless I'm missing something, which is quite possible. The order in which you set up mocks with calledWith matters.

A simple example:

describe('A simple example', () => {
  let mockHttpClient: MockProxy<HttpClient>;

  beforeEach(() => {
    mockHttpClient = mock<HttpClient>();
  });

  it('will return the most generic mock value if it\'s defined first', async () => {
    mockHttpClient.get.calledWith(any()).mockResolvedValue('first');
    mockHttpClient.get.calledWith(anyString()).mockResolvedValue('second');
    mockHttpClient.get.calledWith('bar').mockResolvedValue('third');


    await expect(mockHttpClient.get('foo')).resolves.toBe('second');
  });
});

in the example above the mock returns 'first'. In this example it isn't that big of a deal, you could just reorder them and put the more specific ones ontop and the mock works as expected.

However, the use case where I think it becomes problematic is if you want to define the generic mockResolvedValue in a beforeEach for all tests, and then define more specific mockResolvedValues with in a specific test, this behavior will cause an issue in that scenario.

Here's an example of the more problematic use case for reference

describe('A simple example', () => {
  let mockHttpClient: MockProxy<HttpClient>;

  beforeEach(() => {
    mockHttpClient = mock<HttpClient>();
    mockHttpClient.get.calledWith(any()).mockResolvedValue('first');
  });

  it('will return the most generic mock value if it\'s defined first', async () => {
    mockHttpClient.get.calledWith(anyString()).mockResolvedValue('second');
    mockHttpClient.get.calledWith('bar').mockResolvedValue('third');
    await expect(mockHttpClient.get('foo')).resolves.toBe('second');
  });
});

Any insight or suggestions is appreciated. Also, love this library. It's super useful.

derek-pavao avatar Sep 21 '21 15:09 derek-pavao

you can reset mock before initialized for every test

`it('will return the most generic mock value if it\'s defined first', async () => {
    mockReset(mockHttpClient);   // <--- reset mock conditions

    mockHttpClient.get.calledWith(any()).mockResolvedValue('first'); // <--- duplicate if need
    mockHttpClient.get.calledWith(anyString()).mockResolvedValue('second');
    mockHttpClient.get.calledWith('bar').mockResolvedValue('third');
    await expect(mockHttpClient.get('foo')).resolves.toBe('second');
  });`

kostuyn avatar Oct 22 '21 17:10 kostuyn

Maybe I'm missing something, but this kinda renders beforeEach useless in this scenario doesn't it? I had expected that calledWith would choose the most specific match based on some set of rules. Maybe that wasn't the original intent, but it seems useful.

derek-pavao avatar Nov 22 '21 14:11 derek-pavao

For single test - yes, useless. In other case you can set default order and reorder them in concrete test case with mockReset(mockHttpClient)

kostuyn avatar Nov 23 '21 10:11 kostuyn