Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jest mocking promise method called wrong number of times

As part of my redux action, it makes several sequential api requests. The apiCall method returns a Promise with some value, and that value is used by a subsequent apiCall to make another request, and so on. I'm using Jest to test these api calls.

const myAPI = {
  apiCall(param: any): Promise<any> {
    return new Promise((resolve, reject) => {
      resolve('result');
    });
  },
};

const serialAPIRequests = () => {
  myAPI.apiCall('first_param')
    .then((result) => {
      console.log(result);
      return myAPI.apiCall(result);
    })
    .then((result) => {
      console.log(result);
      return myAPI.apiCall(result);
    })
    .then((result) => {
      console.log(result);
      return Promise.resolve(result);
    });
};

I am trying to write a test to ensure apiCall has been called the correct number of times and with the right parameters.

describe.only('my test', () => {
  it('should test api stuff', () => {
    myAPI.apiCall = jest.fn()
      .mockReturnValueOnce(Promise.resolve('result1'))
      .mockReturnValueOnce(Promise.resolve('result2'))
      .mockReturnValueOnce(Promise.resolve('result3'));
    serialAPIRequests();
    expect(myAPI.apiCall).toHaveBeenCalledTimes(3);
  });
});

What happens is that Jest report Expected mock function to have been called three times, but it was called one time.

Jest test result shows that

  ● Console

console.log 
  result1
console.log 
  result2
console.log 
  result3

 ● my test › should test api stuff

expect(jest.fn()).toHaveBeenCalledTimes(3)

Expected mock function to have been called three times, but it was called one time.

The fact that console.log showed different values means the mocked return was properly passed through the mock function and it was called 3 times.

What could be causing this and how do I properly test this function?

like image 423
Tony Tang Avatar asked Feb 19 '18 18:02

Tony Tang


2 Answers

Use async/await to test async code. Read more here: https://facebook.github.io/jest/docs/en/tutorial-async.html

describe.only('my test', () => {
      it('should test api stuff', async () => {
        myAPI.apiCall = jest.fn()
          .mockReturnValueOnce(Promise.resolve('result1'))
          .mockReturnValueOnce(Promise.resolve('result2'))
          .mockReturnValueOnce(Promise.resolve('result3'));
        await serialAPIRequests();
        expect(myAPI.apiCall).toHaveBeenCalledTimes(3);
      });
    });
like image 143
ltamajs Avatar answered Sep 20 '22 00:09

ltamajs


Promises are async so by the time you do you check the mock was actually called once.

You could do this instead. Wait for all calls to be done and return a promise to indicate the test is async.

return serialAPIRequests().then(() => {
    expect(myAPI.apiCall).toHaveBeenCalledTimes(3);
})
like image 36
Yury Tarabanko Avatar answered Sep 22 '22 00:09

Yury Tarabanko