2

I'm currently writing a React component in Typescript which makes use of a axios-hooks hook called useAxios. An example of this hook in use is here:

 export const App = () => {
  const [{ data, loading, error }, refetch] = useAxios(
    "https://api.myjson.com/bins/820fc"
  );

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error!</p>;

  return (
    <div>
      <button onClick={e => refetch()}>refetch</button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

const rootElement = document.getElementById("root");
render(<App />, rootElement);

I'm trying to figure out how to write a test where I can mock the useAxios hook. I've tried creating a mock of the underlying axios component but I cant get this working:

import React from "react"
import { render } from "@testing-library/react"
import { Test } from "../test"
import useAxios from "axios-hooks"

jest.mock("axios-hooks")
const mockedAxios = useAxios as jest.Mocked<typeof useAxios>

it("Displays loading", () => {
  // How to mock the return values in the following line?
  // mockedAxios.

  const { getByText } = render(<Test />)

  expect(getByText("Loading...")).toBeDefined()
})

I know that I shouldn't have to mock axios which is the underlying dependency, I should be able to mock useAxios, but I though I'd try anyhow.

I realise that this question has been mentioned many times on SO, but I can find a solution to this particular use case.

Any help greatly appreciated!

4
  • so what happens if you jest.mock("axios-hooks") and later useAxious.mockResolvedValue(dataToRespond)? or do you need dynamic mocking based on URL? Commented Sep 13, 2019 at 15:40
  • As I'm using typescript it doesn't appear that I can do that. I assume that there's a way to do that using TS? Commented Sep 13, 2019 at 15:51
  • so issue is specific to type-check in TS, right? to me it looked like test runs but mock is not working on something like that Commented Sep 13, 2019 at 16:01
  • @skyboyer I've updated my question with an example test. I'm trying to figure out how to mock the hooks return values in TS. Commented Sep 13, 2019 at 16:12

3 Answers 3

2

I figured out how to do this myself. To test the custom hook I did the following:

import * as useAxios from "axios-hooks"
jest.mock("axios-hooks")
const mockedAxios = useAxios as jest.Mocked<typeof useAxios>

it("Displays loading message", async () => {

  // Explicitly define what should be returned
  mockedAxios.default.mockImplementation(() => [
      {
        data: [],
        loading: true,
        error: undefined
      },
      () => undefined
    ])

  const { getByText } = render(<Test />)

  expect(getByText("Loading...")).toBeDefined()
})
Sign up to request clarification or add additional context in comments.

Comments

1

Mock the module and setup the expected result of useAxios per test e.g.

jest.mock('axios-hooks');

import useAxios from 'axios-hooks';

test('App displays loading when request is fetching', () => {
  useAxios.mockReturnValue(Promise.resolve({ loading: true }));
  // mount component
  // Verify "Loading" message is rendered
});

4 Comments

I'm using TypeScript and that answer doesn't appear to work
@BenSmith TS shouldn't really come into it, what exactly doesn't work?
It appears it does. Also see my updated question which has an incomplete test written in TS.
@BenSmith TS should be non-intrusive though (unless that's changed), so the above code should run. Looking at your example you've added const mockedAxios = useAxios as jest.Mocked<typeof useAxios> which takes us into TS territory, is that really necessary though? Have you tried the code as-is without any TS?
1

I had to jump through some additional hoops to get the compiler happy with the () => undefined parameter. I'm not a fan of the double as, but I'm not sure how to make it less verbose as I'm new to TS.

import * as useAxios from 'axios-hooks';
import { AxiosPromise } from 'axios';
import React from 'react';
import Test from 'components/Test';
import { render } from '@testing-library/react';

jest.mock('axios-hooks');
const mockedUseAxios = useAxios as jest.Mocked<typeof useAxios>;

it('renders a loading message', async () => {
  mockedUseAxios.default.mockImplementation(() => [
    {
      data: [],
      loading: true,
      error: undefined,
    },
    (): AxiosPromise => (undefined as unknown) as AxiosPromise<unknown>,
  ]);

  const { getByText } = render(<Test />);

  expect(getByText('Loading...')).toBeDefined();
});

3 Comments

Your answer is exactly the same as mine bar the change in the returned function. Why didn't you just make a comment?
I considered this, but my answer also includes an additional import statement as well as the relevant imports missing from your example. I really appreciate you asking the question here and providing a good starting point for troubleshooting. Sometimes it's nice to have an answer with holistically sound approach in a single markup instance.
Ok fair point nicely made and Im glad I could help. I often refer to my own answer on mocking this library as it gets me every time!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.