reactjsreact-hooksreact-testing-librarymsw

React MSW - my code does not mock and actual API gets called


I am trying to test the below component App.js. The component is a simple one that sends a GET request using fetch and displays the result.

import React, { useState, useEffect } from "react";

const App = () => {
  const [users, setUsers] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchAllUsers = async () => {
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/users"
        );
        const data = await response.json();
        setUsers(data);
      } catch (err) {
        setError("Something went wrong!");
      }
    };
    fetchAllUsers();
  }, []);

  return (
    <>
      <h1>List of Users</h1>
      {error && <div>{error}</div>}
      {users ? (
        <ul>
          {users.map((user) => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      ) : (
        <p>No users found</p>
      )}
    </>
  );
};

export default App;

The below is my App.test.js. I did this with the help of tutorials.

import { render, screen } from "@testing-library/react";
import { rest } from "msw";
import { setupServer } from "msw/node";
import App from "./App";

const server = setupServer(
  rest.get("https://jsonplaceholder.typicode.com/users", (req, res, ctx) => {
    res(ctx.json(["Hello World"]));
  })
);

beforeAll(() => {
  server.listen();
});

afterAll(() => {
  server.close();
});

test("empty", async () => {
  render(<App />);
  expect(screen.getByText("List of Users")).toBeInTheDocument();
  await screen.findByText("Hello world");
});

My test case fails with the below details.

Unable to find an element with the text: Hello world. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

Ignored nodes: comments, script, style
<body>
  <div>
    <h1>
      List of Users
    </h1>
    <ul>
      <li>
        Leanne Graham
      </li>
      /*skipping a few lines of HTML*/          
      <li>
        Clementina DuBuque
      </li>
    </ul>
  </div>
</body>

I can see that mocking hasn't happened and the original API is called. Please help me find my mistake.


Solution

  • You forgot to return the res(ctx.json(['Hello World']));. Otherwise, msw will throw an warning:

    [MSW] Expected a mocking resolver function to return a mocked response Object, but got: undefined. Original response is going to be used instead.

    e.g.

    App.tsx:

    import React, { useState, useEffect } from 'react';
    
    const App = () => {
        const [users, setUsers] = useState(null);
        const [error, setError] = useState('');
    
        useEffect(() => {
            const fetchAllUsers = async () => {
                try {
                    const response = await fetch('https://jsonplaceholder.typicode.com/users');
                    const data = await response.json();
                    setUsers(data);
                } catch (err) {
                    setError('Something went wrong!');
                }
            };
            fetchAllUsers();
        }, []);
    
        console.log(users);
    
        return (
            <>
                <h1>List of Users</h1>
                {error && <div>{error}</div>}
                {users ? <div>{users[0]}</div> : <p>No users found</p>}
            </>
        );
    };
    
    export default App;
    

    App.test.tsx:

    import { render, screen } from '@testing-library/react';
    import { rest } from 'msw';
    import { setupServer } from 'msw/node';
    import App from './App';
    import React from 'react';
    import '@testing-library/jest-dom';
    
    const server = setupServer(
        rest.get('https://jsonplaceholder.typicode.com/users', (req, res, ctx) => {
            return res(ctx.json(['Hello World']));
        }),
    );
    
    beforeAll(() => {
        server.listen();
    });
    
    afterAll(() => {
        server.close();
    });
    
    test('empty', async () => {
        render(<App />);
        expect(screen.getByText('List of Users')).toBeInTheDocument();
        expect(await screen.findByText('Hello World')).toBeInTheDocument();
    });
    

    jest.config.js:

    module.exports = {
      preset: 'ts-jest/presets/js-with-ts',
      testEnvironment: 'jsdom',
      setupFilesAfterEnv: [
        'jest-extended',
        './jest.setup.js'
      ],
    };
    

    jest.setup.js:

    import 'isomorphic-fetch';
    

    Test result:

     PASS  stackoverflow/76998600/App.test.tsx (6.264 s)
      ✓ empty (48 ms)
    
      console.log
        null
    
          at App (stackoverflow/76998600/App.tsx:20:10)
    
      console.log
        [ 'Hello World' ]
    
          at App (stackoverflow/76998600/App.tsx:20:10)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |   93.75 |       75 |     100 |   93.33 |                   
     App.tsx  |   93.75 |       75 |     100 |   93.33 | 14                
    ----------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        6.492 s
    

    package version:

    "msw": "^0.29.0",
    "jest": "^26.6.3",
    "@testing-library/react": "^11.2.7",
    "@testing-library/jest-dom": "^5.16.5",
    "isomorphic-fetch": "^3.0.0",