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

Executing the same call twice with different results

Open KDederichs opened this issue 6 years ago • 9 comments

Hey,

I'm trying to write a test for a network call that retries itself after it gets a response with an error code.

The whole thing looks like this:

  for (let i = 0; i < maxRetries; i++) {
    try {
        return yield apiCall;
    } catch (err) {
      if (i < maxRetries) {
        yield call(delay, retryDelay);
      } else {
        throw err;
      }
    }
  }

I set up the mock like this:

      fetch
        .once(addressUserFailure, {
          headers: { 'Content-Type': 'application/json' },
          status: 502,
        }).once(addressUserFailure, {
          headers: { 'Content-Type': 'application/json' },
          status: 200,
        });

My problem is now that mockResponseOnce as well as mockResponses will always return the first 502 response, causing the test to fail. Is this intended behavior?

KDederichs avatar Dec 06 '18 13:12 KDederichs

Where are you using mockResponses?

jefflau avatar Dec 10 '18 12:12 jefflau

I tried the same I did with the example above using mockResponses to see if that changes the result. It does not. Another thing to note is that fetch.mock.calls.length is always 1 even though the network call is called multiple times

KDederichs avatar Dec 10 '18 12:12 KDederichs

That is definitely not expected behavior. I’ve never used it with generators though. What does your whole test look like? After you mock it, how do you call your function to test?

jefflau avatar Dec 10 '18 12:12 jefflau

The test looks something like this:

     fetch.once('', {
        headers: { 'Content-Type': 'application/json' },
        status: 503,
      });
      fetch.once(addressDeleteSuccess, {
        headers: { 'Content-Type': 'application/json' },
        status: 200,
      });
      await sagaTester.dispatch(setAccessToken('123'));
      await sagaTester.dispatch(deleteAddress(addressData));
      await sagaTester.waitFor(ADDRESS_API_REQUEST_SUCCESS);
      expect(sagaTester.getLatestCalledAction()).toEqual(
        addressApiRequestSuccess(),
      );
      expect(fetch.mock.calls.length).toEqual(2);
      expect(sagaTester.wasCalled(FETCH_CUSTOMER)).toEqual(true);

The saga calls

    const result = yield call(
      retryWrapper,
      graphQLClient.request(customerAddressUpdate, variables),
    );

which feeds the graphQL call into the retry loop I posted above.

KDederichs avatar Dec 10 '18 13:12 KDederichs

I came back to look at this again. I just realised what your code is doing. Can you try doing

      fetch
        .mockReject(new Error('rejected')
        .once(addressUserFailure, {
          headers: { 'Content-Type': 'application/json' },
          status: 200,
        });

I think the problem you are having is that it will never get to the catch as you aren't throwing an error

jefflau avatar Dec 15 '18 18:12 jefflau

That fixed half of my problem I'd say. Now the responses will correctly cycle through but it still doesn't increase the fetch.mock.calls.length constantly being 1. Unless I misunderstood what this is used for?

KDederichs avatar Dec 17 '18 11:12 KDederichs

It definitely should not be 1.

Can you create replicate the bug in a simplified repo?

jefflau avatar Dec 19 '18 01:12 jefflau

Sorry, took me a bit but here is a repo with one simple test case showing that behavior: https://github.com/KDederichs/fetch_mock_test

KDederichs avatar Dec 27 '18 12:12 KDederichs

I've PR'd your repo with an async await example.

I'm not familiar with sagas, so I couldn't really debug yours for you. But it feels like the initial call uses the fetch mock, but the calls after don't seem to call fetch?

With async await I seem to be getting the calls in the fetch mock

jefflau avatar Dec 30 '18 02:12 jefflau