DEV Community

Discussion on: The only 3 steps you need to mock an API call in Jest

Collapse
 
chiubaca profile image
Alex Chiu • Edited

Thanks for this, very useful. I'm having a bit of trouble with this though...
my mockResolvedResponse is being returned undefined and I have no idea why!

import axios from "axios";
import { Users } from "./api-call"

jest.mock('axios')

describe('axios tests with mocking', () => {
  test('should fetch posts', async () => {

    const fakeResp = [
      {
        "userId": 1,
        "id": 2,
        "title": "qui est esse",
        "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
      }]

    mockedAxios.get.mockRejectedValue('Network error: Something went wrong');
    mockedAxios.get.mockResolvedValue(fakeResp)

    const axiosSpy = spyOn(mockedAxios, 'get')

    const result = await Users.all()

    expect(result).toEqual(fakeResp)  //❌Fails....
    expect(axiosSpy).toHaveBeenCalledTimes(1)  //✔Passes!

  });

});
Enter fullscreen mode Exit fullscreen mode

any ideas if I'm doing something silly?

Collapse
 
zaklaughton profile image
Zak Laughton • Edited

Sure! I'm not sure exactly what the root cause is, but I've got some troubleshooting steps to start.

My observations

jest.mock() vs jest.spyOn()

Looks like here you are using jest.mock() and jest.spyOn() here on the same function. Usually, these are used interchangeably, but not together. Both functions let you inspect how the function was called. The difference between the 2 is that jest.mock() completely blows away the original function being mocked, while jest.spyOn() keeps the original implementation so the function runs as it is was written. In most cases, I find I only need jest.mock(). (This article seems to do a good job diving into the comparison a bit more Understanding Jest mocks)

Here, it looks like you're spying on your mock, which is redundant, and might have unpredictable results. I'm not sure if that's the issue here, but it's a layer of complexity I'd take out. You should be able to check on the number of calls without the spy (see my suggestion in "What I'd do" below).

Mocking resolved and rejected values

Here, you're using mockedRejectedValue() and mockResolvedValue() on the same function:

mockedAxios.get.mockRejectedValue('Network error: Something went wrong');
mockedAxios.get.mockResolvedValue(fakeResp)
Enter fullscreen mode Exit fullscreen mode

This is redundant because each one will completely overwrite the mocked implementation, so first you set it to reject (no matter what), then you set it to resolve no matter what. Since your expected output (mockResolvedValue(fakeResp)) comes second, the .mockRejectedValue('Network error: Something went wrong') has no impact here. It won't change the output, but I'd remove it just to reduce the complexity for troubleshooting.

mockRejectedValue() is typically only needed if you are explicitly testing an error state (See also: Jest docs for mockRejectedValue() and mockResolvedValue()).

What I'd do

With the notes above, I'd remove some of the redundant code, then if it's still not working, dig into how the mocked function is being called:

  1. Remove the spyOn()

    - const axiosSpy = spyOn(mockedAxios, 'get')
        [...]
    - expect(axiosSpy).toHaveBeenCalledTimes(1) 
    + expect(axios.get).toHaveBeenCalledTimes(1) 
    
  2. Remove the mockRejectedValue()

    - mockedAxios.get.mockRejectedValue('Network error: Something went wrong');
    
  3. If the issue still isn't resolved, you can dig into what axios.get is being called with and what it's returning:

    console.log("axios.get() called with>>>", axios.get.mock.calls[0]);
    console.log("axios.get() returns>>>", axios.get.mock.results[0]);
    

    This should show exactly how axios.get() is being called in Users.all() (see more details on this type of mock call inspection in the jest docs here: Mock Functions). You can also throw some console.logs in the actual Users.all() function, too, which will also output to the terminal during the test.

Here's an example of what that console.log output looks like when I add it to the sample code from this article:

code screenshot

I hope this helps!

Collapse
 
ramyac032001 profile image
c Ramya

can i except the data in the screen to bind in the front end like
expect(screen.getByText(''my first album')).toBeInTheDocument();
but i try this above line ,it should not work .give any soltuion for that?

Collapse
 
chiubaca profile image
Alex Chiu • Edited

Wooah thanks for such a detailed reply!

I forgot to mention one crucial piece of information. I'm trying to do this with TypeScript! ** plot-twist! **

This means I get errors when trying to use axios.get.mock. I think this why I started playing around with jest spies, as it a bit more of type friendly method of getting the assertion metadata out.

Thread Thread
 
zaklaughton profile image
Zak Laughton

Ah, got it! Yeah, how to type mock functions is not immediately clear. Try this:

(axios.get as jest.Mock).mockResolvedValue(fakeResp)
Enter fullscreen mode Exit fullscreen mode

That should at least pass type checking and give you the auto-complete in your editor for mock functions. This should be good enough to at least get it working. If I remember correctly though, it won't actually check the types on the resolved value, so fakeResp could be any type, even if it doesn't match the return type of Users.all(). You'll also have to add as jest.Mock everywhere you call axios.get

If you want stricter typing for this without needing to cast as jest.Mock each time, I've had a great experience with ts-jest. Looks like they've updated a lot since I used it last, so I can't give a syntax example, but you can check out their docs.

tl;dr: use (axios.get as jest.Mock) for generic mock function types, or use a tool like ts-jest for stricter types of that specific mock function.

Thread Thread
 
chiubaca profile image
Alex Chiu

Thank you so much! Was finally able to get the test passing! The trick of using (axios.get as jest.Mock) was the key to letting me debug this thoroughly.