knee-cola / jest-mock-axios

Axios mock for Jest
252 stars 42 forks source link

issue: two axios calls in tested function but only one (first) response ever can be referenced #58

Closed barteklecki closed 4 years ago

barteklecki commented 4 years ago

Hi guys, thanks for cool lib!

I'm trying to test async service that is returning response object and includes few intertwined API calls (axios with interceptors). Right now I'm using jest-mock-axios lib:

(I removed irrelevant parts of code, originally written in TS)

// services/persons.js
import personsAgent from '../agents/persons';
import places from './places';
[...]
const get = async ({ search = '', limit, offset }) => { 
  const places = await places.get({ search: '', limit: 1000, offset: 0 });  // api call to endpoint url '/places/'
  const params = {
    search: !!search.length ? search : null,
    limit,
    offset,
  };
  [...]
  return personsAgent.getAll({ ...params }).then(resp => {
    const results = sort(resp.data.results, .....).map((person, i) => {
      const place = places?.data?.results.filter(.....);

      return {
        id: person.id,
        name: person.first_name,
        surname: person.last_name,
        place,
      };
    });

    return {
      data: { ...resp.data, results },
      status: resp.status,
    };
  });
};
[....]
export default {
  get,
};
// agents/persons.js
import requests from '../utils/axios'; 
export default {
  getAll: (params: object) => requests.get('/persons/', { params }),
}
// services/persons.test.js
import mockAxios from 'jest-mock-axios';
import persons from './persons';

afterEach(() => {
  mockAxios.reset();
});

it('returns Persons data from API', async () => {
    let catchFn = jest.fn(),
      thenFn = jest.fn();

    persons
      .get({ search: '', limit: 10, offset: 0 })
      .then(thenFn)
      .catch(catchFn);

    expect(mockAxios.get).toHaveBeenCalledWith('/persons/', {
      params: { search: null, limit: 10, offset: 0 },
    }); // FAIL - received: '/places/', { search: null, limit: 1000, offset: 0 }

    let responseObj = {
      data: {
        results: ['test'],
      },
    };

    mockAxios.mockResponse(responseObj);

    expect(thenFn).toHaveBeenCalledWith({
      data: {
        results: ['test'],
      },
      status: 200,
    });

    expect(catchFn).not.toHaveBeenCalled();
  });

I'm using axios mock and for my others, simpler tests of services but without additional, internal calls and everything is working fine, but this one is problematic. How to ignore or mock const places = await places.get() to focus on personsAgent.getAll()? Issue right now is that I'm testing request for const places = await places.get() and there is no secondary request for personsAgent.getAll(). axios.getReqByUrl('/persons/') // = null

Any ideas? Thx in advance!

kingjan1999 commented 4 years ago

Hi,

thank you for reporting! It looks like this is a duplicate of #46 and #44, but I'll look deeper into this tomorrow on Friday and see what we can do about it, as it comes up regularly here.

kingjan1999 commented 4 years ago

Hi,

so I looked further into this:

You need to mock a (dummy) response for the first request (places.get) as well. Obviously the code can't proceed further until there is some kind of result from this call as it might be needed for a subsequent call.

So modifying your code, this should work:

it('returns Persons data from API', async () => {
    let catchFn = jest.fn(),
      thenFn = jest.fn();

    persons
      .get({ search: '', limit: 10, offset: 0 })
      .then(thenFn)
      .catch(catchFn);

   mockAxios.mockResponse({data: '<whatever is needed here>'}); // mock first call
   // might need to flush promises here

    expect(mockAxios.get).toHaveBeenCalledWith('/persons/', {
      params: { search: null, limit: 10, offset: 0 },
    }); // FAIL - received: '/places/', { search: null, limit: 1000, offset: 0 }

    let responseObj = {
      data: {
        results: ['test'],
      },
    };

    mockAxios.mockResponse(responseObj);

    expect(thenFn).toHaveBeenCalledWith({
      data: {
        results: ['test'],
      },
      status: 200,
    });

    expect(catchFn).not.toHaveBeenCalled();
  });

Please test and report back; closing this for now.