'Mocking axios.post response triggered by button in react component

I would like to test (with RTL and jest) the api.post (with axios) triggered by a button in my react component bellow. It's about a login display with 2 inputs fields (email and password) and 2 buttons (enter and register). Both the enter and the register buttons called asynchronous functions.


    // src > pages > Login
    
    import React, { useState } from 'react';
    import { useDispatch } from 'react-redux';
    import { useNavigate } from 'react-router-dom';
    import { saveToken } from '../app/slices/tokenSlice';
    import { saveUserData } from '../app/slices/userSlice';
    import api from '../services/api';
    
    function Login() {
      const [loginState, setLoginState] = useState({
        email: '',
        password: '',
      });
    
      const navigate = useNavigate();
      const dispatch = useDispatch();
    
      const handleChange = ({ target: { name, value } }) => {
        setLoginState({
          ...loginState,
          [name]: value,
        });
      };
    
      const enterUser = async () => {
        await api.post('/login', { ...loginState })
          .then((response) => {
            dispatch(saveToken(response.data.token));
            dispatch(saveUserData(loginState));
            navigate('/home');
          })
          .catch((error) => {
            alert(error.message);
          });
      };
    
      const registerUser = async () => {
        api.post('/user', loginState)
          .then(() => {
            alert('Usuário cadastrado com sucesso!');
          })
          .catch((error) => {
            alert(error.message);
          });
      };
    
      return (
        <div>
          <label htmlFor="email">
            Email:
            <input
              id="email"
              name="email"
              onChange={handleChange}
              placeholder="Email"
              type="text"
              value={loginState.email}
            />
          </label>
          <label htmlFor="password">
            Senha:
            <input
              id="password"
              name="password"
              onChange={handleChange}
              type="password"
              value={loginState.password}
            />
          </label>
          <button
            name="btnEnterUser"
            type="button"
            onClick={() => enterUser()}
          >
            Entrar
          </button>
          <button
            name="btnRegisterUser"
            type="button"
            onClick={() => registerUser()}
          >
            Cadastrar
          </button>
        </div>
      );
    }
    
    export default Login;


    // src > services > api.js
    
    import axios from 'axios';
    
    const { BASE_URL } = process.env;
    
    const api = axios.create({
      baseURL: BASE_URL,
    });
    
    api.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8';
    api.defaults.headers.post['Access-Control-Allow-Origin'] = '*';
    
    export default api;

How can i develop a test with mock data related to "registerUser" and "enterUser" functions. I have tried many options with jest.fn(), jest.spyOn() and userEvent.click(btnRegister), where btnRegister is the element which i getByRole as you can check in my test file bellow:


    // src > tests > login.test.js
    
    import React from 'react';
    import '@testing-library/jest-dom';
    import { screen } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import Login from '../pages/Login';
    import renderWithRouter from './renderWithRouter';
    import api from '../services/api';
    
    describe('Componente Login', () => {
      let inputEmail;
      let inputPassword;
      let btnRegister;
      let btnEnter;
    
      const userEmail = '[email protected]';
      const userPassword = '123456';
    
      const tokenMock = {
        token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjA4MWYyYzBhMWFhNzFmYjhjZjU2NjAiLCJlbWFpbCI6Im1hcmlhbmFAZ21haWwuY29tIiwiaWF0IjoxNjQ1MzIyMjU1LCJleHAiOjE2NDUzNDAyNTV9.TIgJFIzg1W0bisvJ3CfRsVCZr3kbKn13_NBN-Ah1U1w',
      };
    
      const userRegisteredResponseMock = {
        user:
        {
          email: '[email protected]',
          id: '6211a1d3eb25fc2418dec05a',
        },
      };
    
      beforeAll(() => {
        renderWithRouter(<Login />);
        inputEmail = screen.getByLabelText(/email/i);
        inputPassword = screen.getByLabelText(/senha/i);
        btnRegister = screen.getByRole('button', { name: /cadastrar/i });
        btnEnter = screen.getByRole('button', { name: /entrar/i });
        userEvent.type(inputEmail, userEmail);
        userEvent.type(inputPassword, userPassword);
      });
    
      it('Registro de usuário com sucesso', async () => {

        // ************** DOESNT WORK **********************

        // api.post = jest.fn().mockImplementation(() => {
        //   Promise.resolve(userRegisteredResponseMock);
        // });
    
        // api.post = jest.fn(() => Promise.resolve(userRegisteredResponseMock));    

        // api.post = jest.fn().mockResolvedValue(userRegisteredResponseMock);
    
        // jest.spyOn(Login, 'enterUser');

        // ****************************************************

        userEvent.click(btnRegister);
    
        expect(<"REGISTER USER" FUNCTION>).toBeCalledTimes(1);

      });
    });

I also have tried created "mocks" folder as jest documentation has mentioned in this link: https://jestjs.io/docs/manual-mocks , but without success.



Solution 1:[1]

in order to force jest using the mocked module, the jest.mock() function should be called.

import api from '../services/api';
...

jest.mock('../services/api');

...

api.post.mockResolvedValue(userRegisteredResponseMock);

it's also possible to mock axios module itself. furthermore, there's a jest-mock-axios npm module designed to achieve the behaviour.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Yone