Search code examples
reactjstestingreact-hooksmockingreact-testing-library

useFetch.ts hook react test mock


There is a great typescript fetch hook I'd like to mock it. Here: https://usehooks-ts.com/react-hook/use-fetch

My app basically looks like this:

export default function App() {
  const { data, error } = useFetch<InterfaceOfTheResponse>(FETCH_URL)

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

  return (
    <div className="App">
       <h1>Welcome</h1>
       //data.map... etc
    </div>
  )
}

My test looks like this:

import { mockData } from "../__mocks__/useFetch"
const mockConfig = {
  data: mockData,
  error: false,
}

jest.mock("../../customHooks/useFetch", () => {
  return {
    useFetch: () => mockConfig
  }
})

describe("Main page functionality", () => {
  test("Renders main page, Welcome", async () => {
    const { findByText } = render(<App />)
    const WELCOME = await findByText(/Welcome/)
    expect(WELCOME).toBeInTheDocument()
  })
})

I've tried a couple of ways to mock it, this is the closest what I think it should work, but it's (obviously) not. It says, displays (in the test screen.debug()) the "Error" if statement, and even when I left out form the component the if error check, the "data" is undefined. So what am I doing wrong?


Solution

  • Don't mock the implementation of the useFetch hook, you may break its functions. Instead, we should mock the fetch API of the browser and its response.

    E.g.

    App.tsx:

    import React from 'react';
    import { useFetch } from 'usehooks-ts'
    
    const FETCH_URL = 'http://localhost:3000/api';
    export default function App() {
      const { data, error } = useFetch<any[]>(FETCH_URL)
      console.log(data, error);
    
      if (error) return <p>Error</p>
      if (!data) return <p>Loading...</p>
    
      return (
        <div className="App">
          <h1>Welcome</h1>
          {data.map(d => <div key={d}>{d}</div>)}
        </div>
      )
    }
    

    App.test.tsx:

    import { render, screen } from "@testing-library/react";
    import '@testing-library/jest-dom';
    import React from "react";
    import App from './App';
    
    describe('74144869', () => {
      test('should pass', async () => {
        const mData = [1, 2]
        const mResponse = {
          ok: true,
          json: jest.fn().mockResolvedValue(mData)
        }
        global.fetch = jest.fn().mockResolvedValue(mResponse as unknown as Response);
        render(<App />);
        expect(await screen.findByText(1)).toBeInTheDocument();
      })
    });
    

    Test result:

    PASS  stackoverflow/74144869/App.test.tsx (11.11 s)
      74144869
        ✓ should pass (58 ms)
    
      console.log
        undefined undefined
    
          at App (stackoverflow/74144869/App.tsx:7:11)
    
      console.log
        undefined undefined
    
          at App (stackoverflow/74144869/App.tsx:7:11)
    
      console.log
        [ 1, 2 ] undefined
    
          at App (stackoverflow/74144869/App.tsx:7:11)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |   91.67 |       75 |     100 |     100 |                   
     App.tsx  |   91.67 |       75 |     100 |     100 | 9                 
    ----------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        11.797 s, estimated 12 s
    

    package versions:

    "usehooks-ts": "^2.9.1",
    "@testing-library/react": "^11.2.7",
    "@testing-library/jest-dom": "^5.11.6",