Jonathan
Jonathan

Reputation: 32868

Why does mocked axios get method return undefined?

I've written a fairly simple async method that retrieves results over HTTP:

import axios from "axios";

const BASE_URI = "http://api.tvmaze.com";

export const getSearchShows = async (search: string) => {
  const uri = `${BASE_URI}/search/shows?q=${encodeURIComponent(search)}`;
  const response = await axios.get(uri);

  return response.data;
};

I want to unit-test it. So I write the following Jest test, which intends to mocks axios and return fake results, which I can then assert on:

import axios from "axios";

import fakeSearchShowsResponse from "../data/search-shows--q=test.json";

import { getSearchShows } from "./TvShows.http";

jest.mock("axios");

describe("TvShows.http", () => {
  describe("getSearchShows", () => {
    it("retrieves shows over http and correctly deserializes them", async () => {

      const mockAxiosGet = jest.spyOn(axios, "get");

      mockAxiosGet.mockImplementation(async () => fakeSearchShowsResponse);

      const shows = await getSearchShows("test");

      console.log(mockAxiosGet.mock.calls);

      expect(shows[0].id).toEqual(139);

    });
  });
});

I expected that, due to calling jest.mock("axios"), the axios get method would have been replaced with a mocked Jest method.

Furthermore, I expected that due to calling mockAxiosGet.mockImplementation and passing it a function, a call to the axios get method would actually call my mock function, allowing me to substitute test data for real data.

What actually happens is that the call to axios get returns undefined, causing my test assertion to fail.

And yet, oddly enough, the Jest spy still registers that the method was called – the console.log outputs a single call.

So why is this supposedly mocked method returning undefined when I explicitly provided a mock implementation that returns a value?

Or do I misunderstand how mockImplementation is meant to be used?

Upvotes: 4

Views: 8592

Answers (2)

seanplwong
seanplwong

Reputation: 1081

jest.mock('axios') would mock the entire module, replacing everything with a stub. So it doesn't necessarily have relation with the fact that jest.mock() is hoisted, it is only to make sure dependency is mocked before import. It's just the stub returning undefined.

Meanwhile, you could

import axios from 'axios';

jest.mock('axios');

console.log(axios); // mocked

describe('suite', () => {
  it('test', () => {
    axios.get.mockResolvedValue('{"test": "test"}');
    // --------------------------^
    // you class would get this when calling axios.get()

    // you could also do some assertion with the mock function
    expect(axios.get).toHaveBeenCalledTimes(1);
    expect(axios.get).toHaveBeenCallWith('http://some-url');
  });
});

Upvotes: 2

Jonathan
Jonathan

Reputation: 32868

So after some experimentation, it would appear that the jest.mock("axios") call was interfering with the jest.spyOn(axios, "get"); call.

After removing the jest.mock call, it now returns my mocked value from the jest.spyOn call.

I think this might be because the jest.mock call was hoisted, whereas the jest.spyOn call wasn't. So the module under test was going off the hoisted mock rather than the un-hoisted one.

Upvotes: 11

Related Questions