Ben Smith
Ben Smith

Reputation: 20230

How to test a component which is using a custom TypeScript React Hook?

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!

Upvotes: 2

Views: 5155

Answers (3)

jerrypop
jerrypop

Reputation: 19

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();
});

Upvotes: 1

Ben Smith
Ben Smith

Reputation: 20230

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()
})

Upvotes: 2

James
James

Reputation: 82136

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
});

Upvotes: 1

Related Questions