Search code examples
javascriptreactjsreact-testing-libraryjest-mock-axios

MockResolvedValueOnce returns undefined first and then returns the mocked value correctly


I'm trying to mock my axios, but I'm not getting it. The mock is returning undefined when I call the api, and then it returns with the right object. I can't identify the error why when calling the api for the first time it returns undefined if anyone can help me.

It's as if my api, when called, didn't expect the mock result and returned undefined. I'm using useEffect to be able to call the api in my code and it's like it calls the api twice, the first time comes undefined and the second comes with the right result,

//MainPage.test.js

import React from 'react';
import { screen, cleanup } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import mockedAxios from 'axios';
import renderWithRouter from './renderWithRouter';
import MainPage from '../pages/mainPage/MainPage';

const data = [
  {
    id: 3,
    name: 'Ciclano Fake',
    mobile: '11232123123',
    email: 'ciclanofake1@gmail',
  }, {
    id: 2,
    name: 'Fulano Fake',
    mobile: '91232123123',
    email: 'fulanofake1@gmail',
  },
  {
    id: 1,
    name: 'Algúem Fake',
    mobile: '31232123123',
    email: 'alguemfake1@gmail',
  }];

describe('Testa página prinicpal que lista os contatos', () => {
  afterEach(cleanup);
  it('Testa se ao clicar em voltar o usuário é redirecionado para loginPage', () => {
    const { history } = renderWithRouter(<MainPage />);

    const backBtn = screen.getByRole('link', { name: /seta apontando para o lado esquerdo voltar/i });
    expect(backBtn).toBeInTheDocument();

    userEvent.click(backBtn);
    expect(history.location.pathname).toBe('/');
  });

  it('Testa se a lista de contatos é carregada corretamente na página', async () => {
    mockedAxios.get.mockResolvedValueOnce({ data });
    renderWithRouter(<MainPage />);

    const contactFake1 = await screen.findByRole('cell', {
      name: /Ciclano Fake/i,
    });
    expect(contactFake1).toBeInTheDocument();
  });
});
//src/__mocks__/axios.js


export default {
  get: jest.fn().mockResolvedValue(),
};

//Error message

  ● Console

    console.log
      undefined

      at src/pages/mainPage/MainPage.js:26:15

    console.log
      {
        data: [
          {
            id: 3,
            name: 'Ciclano Fake',
            mobile: '11232123123',
            email: 'ciclanofake1@gmail'
          },
          {
            id: 2,
            name: 'Fulano Fake',
            mobile: '91232123123',
            email: 'fulanofake1@gmail'
          },
          {
            id: 1,
            name: 'Algúem Fake',
            mobile: '31232123123',
            email: 'alguemfake1@gmail'
          }
        ]
      }

      at src/pages/mainPage/MainPage.js:26:15

  ● Testa página prinicpal que lista os contatos › Testa se a lista de contatos é carregada corretamente na página

    TypeError: Cannot read property 'data' of undefined

      25 |       const result = await getContact(token);
      26 |       console.log(result);
    > 27 |       if (!result.data) return;
         |                   ^
      28 |       setContactList(result.data);
      29 |     })();
      30 |   }, [change]);

      at src/pages/mainPage/MainPage.js:26:15
//MainPage.js Component under test

import React, { useEffect, useState, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import BtnHeaderTable from '../../components/btnHeaderTable/BtnHeaderTable';
import Header from '../../components/header/Header';
import './mainPageStyle.scss';
import trash from '../../assets/images/trash.svg';
import editIcon from '../../assets/images/edit.svg';
import { getContact } from '../../service/api';
import RemoveModal from '../../components/removeModal/removeModal';
import MyContext from '../../context/MyContext';
import getToken from '../../service/localStorage';

function MainPage() {
  const navigate = useNavigate();
  const [contactList, setContactList] = useState([]);
  const {
    modal, setModal, filter,
  } = useContext(MyContext);
  const [change, setChange] = useState(false);

  useEffect(() => {
    (async () => {
      const token = getToken();
      const result = await getContact(token);
      console.log(result);
      if (!result.data) return;
      setContactList(result.data);
    })();
  }, [change]);

  function createContactBtn() {
    navigate('/createContact');
  }

  function editBtn(id) {
    navigate(`/editContact/${Number(id)}`);
  }

  function openRemoveModal(id, name) {
    setModal({
      open: true,
      id,
      name,
    });
  }

  function filterResults() {
    const titles = ['id', 'name', 'mobile', 'email'];
    const [selectFilter] = titles.filter((item) => filter[item] === true);
    if (!selectFilter) return contactList;
    const cloneContacts = [...contactList];
    const sortContacts = cloneContacts.sort((a, b) => {
      if (a[selectFilter] > b[selectFilter]) {
        return 1;
      }
      if (a[selectFilter] < b[selectFilter]) {
        return -1;
      }
      return 0;
    });
    return sortContacts;
  }

  return (
    <main className="contactList-container">
      {
        modal.open && <RemoveModal change={change} setChange={setChange} />
      }
      <Header path="/" />
      <section className="list-container">
        <div className="header-list">
          <h1>Listagem de Contatos</h1>
          <button
            type="button"
            onClick={createContactBtn}
          >
            Adicionar novo contato
          </button>
        </div>
        <table className="table">
          <thead className="th-head">
            <tr>
              <th>
                <BtnHeaderTable
                  title="#"
                  keyObj="id"
                />
              </th>
              <th>
                <BtnHeaderTable
                  title="Nome"
                  keyObj="name"
                />
              </th>
              <th>
                <BtnHeaderTable
                  title="Celular"
                  keyObj="mobile"
                />
              </th>
              <th>
                <BtnHeaderTable
                  title="Email"
                  keyObj="email"
                />
              </th>
              <th>
                <p>Ações</p>
              </th>
            </tr>
          </thead>
          <tbody className="tb-body">
            {
              filterResults().map((contact) => (
                <tr key={contact.id}>
                  <td>{contact.id}</td>
                  <td>{contact.name}</td>
                  <td>{contact.mobile}</td>
                  <td>{contact.email}</td>
                  <td className="btn-body">
                    <button
                      type="button"
                      name={contact.id}
                      onClick={() => openRemoveModal(contact.id, contact.name)}
                    >
                      <img src={trash} alt="Lata de lixo simbolizando uma remoção" />
                      Remover
                    </button>
                    <button
                      type="button"
                      onClick={() => editBtn(contact.id)}
                    >
                      <img src={editIcon} alt="Papel e caneta simbolizando uma edição" />
                      Editar
                    </button>
                  </td>
                </tr>
              ))
            }
          </tbody>
        </table>
      </section>
    </main>
  );
}

export default MainPage;

Solution

  • I found the reason to return undefined, I'm rendering the first test without mocking the return of my api, so it returns undefined in the first test and in the second test that I mock returns the correct value.