DEV Community

Cover image for Como testar aplicações com React Testing Library e Jest
Ivan Trindade
Ivan Trindade

Posted on

Como testar aplicações com React Testing Library e Jest

Como desenvolvedores de software, é fundamental reduzir as chances de falha de nosso programa executando testes com o usuário final em mente e testando sua aplicação da mesma forma que ele o usaria.

É impraticável esperar que os componentes de uma aplicação funcionem de forma confiável o tempo todo. Quando ocorre uma modificação ou ajuste em um pedaço de código existente, as coisas podem ou não quebrar, e é melhor presumir o pior. Além disso, se tivermos testes automatizados desenvolvidos para vários componentes em nossa aplicação, podemos perceber imediatamente quando uma alteração quebra algo em nossa aplicação - antes de liberarmos nosso código para produção.

Teste é o processo de avaliar um sistema ou seus componentes com a intenção de descobrir se ele satisfaz os requisitos especificados ou não. Em palavras simples, testar é executar um sistema para identificar quaisquer lacunas, erros ou requisitos ausentes contrários aos requisitos reais.

O que abordaremos nesse artigo:

  • Resumo rápido
  • Testes automatizados
  • Por que você deve escrever testes?
  • Tipos de testes automatizados
  • Desenvolvimento Orientado a Testes (TDD)
  • React Testing Library (RTL)
  • Compreendendo um bloco de testes
  • Mini projeto
  • Conclusão

Resumo rápido

Esse artigo explica o que é teste automatizado, por que escrever testes para aplicações é importante, as diferentes formas de teste automatizado, Test Driven Development (TDD), React Testing Library (RTL) e construiremos um miniprojeto com uma abordagem TDD.

Esse artigo é adequado para aqueles que nunca escreveram uma única linha de código de teste antes; Embora para obter o máximo desta parte, você deve estar familiarizado com como configurar e navegar em um novo projeto React, bem como interagir com um gerenciador de pacotes. Também é necessário estar familiarizado com o axios.

Testes Automatizados

Testes automatizados são trechos de código que executam seu código e verificam se a saída dessa execução de código atende a uma determinada expectativa. Se você escrever testes automatizados para sua aplicação, poderá detectar problemas rapidamente quando começar a trabalhar em seu código e um ou mais testes falharem.

O teste manual, por outro lado, é o que fazemos quando clicamos em nossa aplicação no navegador, avaliando diferentes recursos.

Por que você deve escrever testes?

  • AJuda a detectar bugs antecipadamente e permite refatorar seu código.
  • Garante que sua aplicação satisfaça as especificações do projeto.
  • Reduz o tempo de teste manual (quando um usuário real usa nossa aplicação para testar sua funcionalidade).
  • Envio de código eficiente e de qualidade para a produção.
  • Melhorar a manutenibilidade do código.

Muitas vezes, acredita-se que o teste seja um processo demorado; escrevendo testes === mais código. Por outro lado, todo desenvolvedor deve estar familiarizado com os fundamentos do teste. Além disso, quem entende seu código melhor do que você 🙃?

Escrever testes automatizados é metade do esforço do desenvolvedor para resolver o problema. Saber como testar seu código é um ótimo método para avançar como desenvolvedor, pois permite que você descubra coisas novas sobre seu código e aprimore suas habilidades de debugar seu código. 🚀.


Tipos de testes automatizados

1. Testes unitários

Os testes unitários são a forma mais simples de testes automatizados, em que funções, métodos, componentes ou unidades individuais de uma aplicação são testados isoladamente para garantir que funcionem corretamente.

Por exemplo, em uma aplicação de tarefas, você pode escrever um teste de unidade para o componente de input, para confirmar que o usuário pode digitar nele e alterar seu valor.

2. Teste de integração

O teste de integração é um tipo de teste de software no qual várias unidades que trabalham juntas são testadas como um todo. Essa forma de teste é usada para garantir que duas ou mais unidades/módulos possam se comunicar efetivamente entre si.

Por exemplo , em uma aplicação de tarefas, onde existem três componentes principais: um elemento input onde uma nova tarefa é digitada, um botão para adicionar, quando clicado, adiciona a nova tarefa à lista de tarefas e um elemento div que renderiza a lista de tarefas. Como esses três componentes são agrupados logicamente, um teste de integração pode ser escrito para garantir que eles funcionem bem juntos.

3. Teste de ponta a ponta (E2E)

O teste de ponta a ponta é uma técnica de teste de software que exige que testemos toda a aplicação do início ao fim em um ambiente de produção. É executado em um ambiente simulado que emula o comportamento de um usuário. Ferramentas como Cypress, Selenium e Puppeteer podem ser usadas para testes E2E.

4. Teste de aceitação

O teste de aceitação é realizado pelo cliente/proprietário da empresa para garantir que a aplicação atenda a todos os requisitos.

5. Teste de fumaça

O teste de fumaça é usado para confirmar que as funcionalidades fundamentais do sistema em teste estão funcionando corretamente e em alto nível.

Outros testes de software não funcionais incluem testes de segurança, desempenho, usabilidade e compatibilidade.


Desenvolvimento Orientado a Testes (TDD)

TDD é uma estratégia para descrever o comportamento do seu código antes de ser implementado. Tem o slogan 'Red-Green-Refactor'.

Vermelho - Um caso de teste automatizado para um recurso específico que (inicialmente) falha é gravado.

Verde - Escreva o código necessário para passar no teste.

Refatorar - refatore ou otimize seu código.

Além do TDD puro, existem outras opções a serem exploradas, principalmente no desenvolvimento front-end.

React Testing Library (RTL)

o React Testing Library, é uma solução de teste leve para componentes React. Essa biblioteca é baseada no DOM Testing Library e é um subconjunto da família de pacotes @testing-library. Seu princípio de orientação é:

Quanto mais seus testes se assemelharem à forma como seu software é usado, mais confiança eles podem lhe dar.

Isso implica que o RTL nos permite testar o comportamento de nossos componentes React da perspectiva dos usuários de nossa aplicação, em vez dos detalhes de implementação do componente. O uso de RTL torna suas aplicações mais acessíveis e ajuda você a aproximar seus testes de como um usuário usaria seus componentes, dando a você mais confiança de que sua aplicação funcionará quando um usuário real o usar.

Frameworks de teste populares dentro da comunidade React são Enzyme e Jest.

RTL é uma biblioteca e não é específica para uma estrutura de teste, embora Jest seja a escolha recomendada, de acordo com a documentação oficial da RTL. Como resultado, iremos combiná-lo com o Jest nesse artigo.

Compreendendo um bloco de teste

Não se preocupe com a configuração porque todo novo projeto React inicializado com create-react-app, RTL e Jest já estão configurados. Se você estiver usando a configuração personalizada do React, use o seguinte comando para instalá-la:

npm install --save-dev @testing-library/react
Enter fullscreen mode Exit fullscreen mode

Inicie uma nova aplicação React com o bloco de código abaixo:

npx create-react-app my-app
Enter fullscreen mode Exit fullscreen mode

Depois que um projeto React é inicializado com create-react-app, um teste de amostra para o arquivo App.js é pré-configurado em um arquivo App.test.js:

Imagem de código

Vamos detalhar a estrutura desse teste:

// React testing methods are imported
import { render, screen } from '@testing-library/react'; 

import App from './App'; // Component to be tested

// Test block for the component
test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

Enter fullscreen mode Exit fullscreen mode
// Os testes são escritos no método test/it fornecido pelo Jest
// Um método de teste leva em dois parâmetros:
// Uma descrição do teste e uma função com lógica para testar

test("", () => {});
// OR
it("", () => {});

Enter fullscreen mode Exit fullscreen mode

Não importa se você usa o método test ou it, no entanto, prefiro o método test.

test('renders learn react link', () => {
  render(<App />); // renders the component into the DOM 
  const linkElement = screen.getByText(/learn react/i); // gets element that has the text 'learn react'
  expect(linkElement).toBeInTheDocument(); // asserts that the element found is rendered 
});

Enter fullscreen mode Exit fullscreen mode
  • O componente a ser testado é renderizado no DOM.
  • Em seguida, o DOM é consultado pelo elemento link com o testo "Aprenda React" usando o objeto screen importado do RTL, que possui várias consultas, no nosso caso a consulta getByText é usada para selecionar o elemento link.

Lembre-se de como getElementById de outros métodos de seleção de DOM Javascript funcionam? Sim!, o RTL possui consultas que podem ser usadas para selecionar elementos do componente a ser testado. Confira uma lista de todas as consultas.

Na última linha de código, usando uma asserção fornecida pelo Jest, testamos se o elemento link é renderizado no DOM.

expect(linkElement).toBeInTheDocument();
Enter fullscreen mode Exit fullscreen mode

Finalmente, para executar nosso teste, execute npm run test ou yarn test e pressione a. Todos os testes no projeto serão executados no modo de observação. Jest executará arquivos que terminam com .spec.js ou .test.js automaticamente.

Imagem de testes passando

Como você notou, nosso teste passou, agora vamos fazê-lo falhar 😈. No arquivo App.js, altere o texto do elemento a para 'Hello World'.

Você deve ver o teste falhar com essa mensagem de erro:

TestingLibraryElementError: Unable to find an element with the text: /learn react/i. 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.

Imagem de código falhando

Isso ocorre porque, no DOM, não há elemento com o texto 'Aprender React'. Se você adicionar um novo elemento com o texto 'Aprender React', o teste passará novamente. Adivinha? você implementou seu primeiro teste unitário!

À medida que progredirmos, criaremos um mini-projeto de Solicitação de amizade, mas, primeiro, gostaria de destacar os vários métodos que a RTL forneceu para selecionarmos elementos do DOM, bem como alguns métodos de asserção do Jest.

Query Selectors

No exemplo inicial, utilizamos o método getByText do RTL, que é um dos métodos de consulta disponíveis. Depois que um elemento é selecionado, podemos fazer várias afirmações ou imitar as interações do usuário para interagir com ele.

Se você ainda não adivinhou, o método getByText seleciona um elemento com base no texto dentro do elemento, independentemente de ser uma div, h1, p, ou um h4 contanto que encontre um elemento que tenha o texto fornecido, ele seleciona esse elemento.

getByText("learn react")

Enter fullscreen mode Exit fullscreen mode

Para selecionar elementos DOM, o RTL inclui uma variedade de métodos de consulta. Cada um dos métodos de consulta se enquadra em um dos seguintes grupos :

  • Elementos únicos: getBy..., findBy..., queryBy...
  • Múltiplos elementos: getAllBy..., findAllBy..., queryAllBy...

Tabela de query selectors

A tabela acima é da documentação oficial da RTL e mostra uma visão geral do que as várias categorias retornam.

Jest matchers

Ao desenvolver testes, geralmente é necessário confirmar se os valores atendem a requisitos específicos. O expect inclui uma variedade de "matchers" que permitem validar coisas diferentes. Mais correspondências personalizadas estão incluídas em um pacote separado chamado jest-dom por RTL.

Dê uma olhada no arquivo package.json, você notará que jest-dom já está instalado como uma dependência.

  "dependencies": {
    "@testing-library/jest-dom": "^5.16.4",

Enter fullscreen mode Exit fullscreen mode

Em nosso teste inicial, utilizamos o matcher toBeInTheDocument para determinar se o linkElement estava ou não no DOM.

expect(linkElement).toBeInTheDocument();

Enter fullscreen mode Exit fullscreen mode

Alguns exemplos:

// toBe
test("Test that name is John", () => {
  const name = "John";
  expect(name).toBe("John");
});

// toEqual
test("Two plus Two equals Four", () => {
  const result = 2 + 2;
  expect(result).toEqual(4);
});

//toContain
test("Vowels", () => {
  const vowels = ["a", "e", "i", "o", "u"];
  expect(vowels).toContain("e");
  //  .not
  expect(vowels).not.toContain("z");
});

Enter fullscreen mode Exit fullscreen mode

Saiba mais sobre os matchers do Jest clicando aqui. Correspondências jest-dom personalizadas podem ser encontradas aqui.

Mini projeto

Para esta seção, criaríamos um aplicativo de solicitação de amizade simples, os dados do usuário serão extraídos desse link

Vamos descrever as especificações da nossa aplicação:

  • Enquanto espera por uma resposta da API, exiba um indicador de carregamento escrito Loading....
  • Exibir solicitações de amizade na tela assim que a chamada da API retornar com um status de 200.
  • Os usuários devem poder recusar solicitações de amizade.
  • Se não houver mais solicitações, um botão de atualização ao clicar, chama a API para buscar novos dados.

Seguindo uma abordagem TDD, escreveríamos o teste antes de construir nosso componente. Crie um novo arquivo src/components/Requests/Requests.test.js.

//   src/components/Requests/Requests.test.js 

import axios from "axios";
import {
  render,
  screen,
  waitForElementToBeRemoved,
  fireEvent
} from "@testing-library/react";
import Requests from "./Requests";

describe("Requests page", () => {
 test("Renders Requests component",  async () => {
    render(<Requests />);

    await Promise.all([
      waitForElementToBeRemoved(() => screen.queryByText(/Loading.../i)),
    ]);
    expect(axios.get).toHaveBeenCalledTimes(1);

  });
});

Enter fullscreen mode Exit fullscreen mode

Nosso bloco de teste está incluído em uma função describe; isso é usado para testes de escopo.

Primeiro, o componente <Requests /> é renderizado; tenha em mente que o objetivo é desenvolver testes que imitem como o software é usado. Antes que os dados sejam buscados, o usuário fica sabendo que está carregando, por causa do indicador de carregamento que aparece Loading... na tela. E depois que a solicitação da API retorna com status 200, o indicador de carregamento não aparece mais na tela.

Portanto, utilizamos o método assíncrono waitForElementToBeRemove denvolvido em um método Promise.all para esperar que Loading... desapareça da tela.
Em seguida, verificamos se a chamada axios.get foi executada apenas uma vez.

Mockando com Jest

Jest nos permite mockar/imitar o comportamento de funções em nossa aplicação; jest.fn() é usado para criar uma função simulada.

Em vez de solicitar dados de nossa API no arquivo de teste, criaremos uma função simulada que retorna dados fictícios semelhantes aos dados retornados da API.

Isso é importante porque estamos testando nossa aplicação React e, se nosso teste enviar uma solicitação para a API real, também estamos testando a API; se a API falhar, nosso teste falhará. Como resultado, diminuindo a confiança em nossa aplicação.

Crie um novo arquivo src/__mocks__/axios.js. Como estamos simulando axios, o nome do arquivo deve ser axios.js, e os arquivos fictícios são armazenados na pasta __mocks__, que é colocada na pasta src.

// src/__mocks__/axios.js
import { mockData } from "../mockData";

const customAxios = {
    get: jest.fn().mockResolvedValue(mockData),
  };

export default customAxios

Enter fullscreen mode Exit fullscreen mode

Importamos mockData e o incluímos para ser retornado quando fizermos a chamada da API em nosso teste. A estrutura de seus dados simulados deve ser semelhante à estrutura dos dados retornados de sua API.

// src/mockData.js

export const mockData = {
  data: {
    results: [
      {
        name: { first: "Alison", last: "Carlson" },
        email: "alison.carlson@example.com",
        login: {
          uuid: "56f8e0af-50bb-483b-ba30",
        },
        picture: {
          medium: "https://randomuser.me/api/portraits/med/women/25.jpg",
        },
      },
      {
        name: { first: "Frederik", last: "Larsen" },
        email: "frederik.larsen@example.com",
        login: {
          uuid: "162b6088-870a-4b12-bcbf",
        },
        picture: {
          medium: "https://randomuser.me/api/portraits/med/men/27.jpg",
        },
      },
    ],
  },
};

// Single Mock Data
export const singleMockData = {
  name: { first: "Alison", last: "Carlson" },
  email: "alison.carlson@example.com",
  login: {
    uuid: "56f8e0af-50bb-483b-ba30",
  },
  picture: {
    medium: "https://randomuser.me/api/portraits/med/women/25.jpg",
  },
};

Enter fullscreen mode Exit fullscreen mode

Execute o teste com npm run test ou yarn test.

Quando executamos nosso teste, o Jest identifica a pasta __mocks__ e utiliza as personalizações de axios que definimos, em vez do axios da pasta node_modules. As chamadas do axios retornam uma promise que resolve os dados que queremos, então seguimos o exemplo e retornamos uma promise contendo os dados que queremos (dados fictícios no arquivo src/mockData.js).

Neste ponto, nosso teste está falhando porque não construímos nossos componentes, então vá para src/components/Requests/Requests.js e cole o bloco de código abaixo:

// src/components/Requests/Requests.js

import React, { useState, useEffect } from "react";
import Request from "../Request/Request";
import axios from "axios";

const Requests = () => {
  const [loading, setLoading] = useState(true);
  const [friendRequests, setFriendRequests] = useState([]);

  useEffect(() => {
    fetchFriendRequests();
  }, []);

  const fetchFriendRequests = async () => {
    try {
      const { data } = await axios.get("https://randomuser.me/api/?results=5");
      setFriendRequests(data.results);
      setLoading(false);
    } catch (error) {
      console.log(error);
    }
  };

  if (loading) {
    return (
      <div className="loading">
        <h1>loading...</h1>
      </div>
    );
  }

  return (
    <section>
      <div className="title">
        <h2>friend requests</h2>
        <div className="underline"></div>
      </div>
      <div>
        {friendRequests.map((request, index) => {
          const { login } = request;
          return <Request index={index} key={login.uuid} {...request} />;
        })}
      </div>
    </section>
  );
};

export default Requests;

Enter fullscreen mode Exit fullscreen mode

Criamos uma função fetchFriendRequests que busca os dados da API e chamamos a função dentro de um bloco useEffect. Quando a API retorna, o estado é atualizado e o carregamento é definido como false.

Como não criamos o componente Request.js, nosso teste ainda falhará, então navegue até src/components/Request/Request.js e cole o bloco de código abaixo:

// src/components/Request/Request.js

import React from "react";

const Request = ({ name, email, login, picture, index, id }) => {
  return (
    <div className="single-request" >
      <div className="single-request-main">
        <div>
          <img src={picture.medium} alt={name.first} />
        </div>
        <div>
          <p>
            <strong>Name</strong>: {name.first + " " + name.last}{" "}
          </p>
          <p>
            <strong>Username</strong>: {login.username}{" "}
          </p>
          <p>
            <strong>Email</strong>: {email}{" "}
          </p>
        </div>
      </div>
    </div>
  );
};

export default Request;

Enter fullscreen mode Exit fullscreen mode

O teste passou!

Imagem dos testes passando

E nosso projeto também está ficando bom em localhost:3000 😎

Imagem do projeto

Vamos adicionar um novo teste em nosso bloco describe para afirmar que o primeiro índice dos dados está no DOM:

describe("Requests page", () => {

  test("Renders Requests component", async () => {
    render(<Requests />);

    await Promise.all([
      waitForElementToBeRemoved(() => screen.queryByText(/Loading.../i)),
    ]);
    expect(axios.get).toHaveBeenCalledTimes(1);
  });

  // new test block
  test("finds first index of Request in the DOM", async () => {
    render(<Requests />);

    const firstRequest = await screen.findByTestId(/request-0/i);

    expect(firstRequest).toBeInTheDocument();
  })
});

Enter fullscreen mode Exit fullscreen mode

Observe que o bloco test recebeu uma função assíncrona? isso é necessário para que possamos usar await dentro da função.

O componente <Requests /> é renderizado, em seguida, encontramos o elemento firstRequest por seu test-id. Nosso teste falhará porque nenhum elemento tem request-0 como test-id.

O que é um test-id?

Test-id é um atributo exclusivo que você pode atribuir a um elemento. Por exemplo:

<div data-testId="unique-attribute">Hello World</div>

Enter fullscreen mode Exit fullscreen mode

Vá para src/components/Request/Request.js e dê ao div pai um test-id exclusivo.

//   src/components/Request/Request.js

<div className="single-request" data-testid={`request-${index}`}>

Enter fullscreen mode Exit fullscreen mode

Vale a pena notar que usamos especificamente findByTestId e isso ocorre porque, ao contrário de getBy... e queryBy..., findBy... retorna uma promise que resolve quando um elemento é encontrado que corresponda à consulta fornecida.

O que significa que se tivéssemos usado getByTestId ou queryByTestId, nosso teste teria falhado:

Imagem de código

Mas como não o fizemos, o teste passou!

Botão Recusar

Em seguida, adicionamos um botão que, ao ser clicado, recusa um pedido de amizade e o remove da tela. Crie um novo arquivo de teste src/components/Request/Request.test.js e cole o código abaixo:

// src/components/Request/Request.test.js

import { render, screen, fireEvent } from "@testing-library/react";
import Request from "./Request";
import { singleMockData } from "../../mockData";

const mockedFn = jest.fn();

describe("Single Request component", () => {
  test("decline button to unmount component", () => {
    const { unmount } = render(
      <Request
        index={0}
        key={0}
        {...singleMockData}
        removefriendRequest={mockedFn}
      />
    );

    const declineButton = screen.getByText(/decline/i);
    fireEvent.click(declineButton);
    unmount();

  });
});

Enter fullscreen mode Exit fullscreen mode

Começamos renderizando o componente, mas como precisaremos da função unmount posteriormente, desestruturamos a partir do método render. <Request /> leva um monte de props, então nós fornecemos as props necessárias; para a propriedade removefriendRequest, passamos uma função fictícia que retorna undefined.

Por fim, localizamos o elemento de botão no DOM usando getByText e acionamos um evento click usando o método fireEvent. Depois que o evento click é acionado, desmontamos <Request /> usando unmount().

Em seguida, vamos atualizar <Requests /> adicionando a função removefriendRequest e passando-a como um prop para <Request />. Fique atento aos comentários no bloco de código abaixo:

// src/components/Requests/Requests.js

import React, { useState, useEffect } from "react";
import Request from "../Request/Request";
import axios from "axios";

const Requests = () => {
  const [loading, setLoading] = useState(true);
  const [friendRequests, setFriendRequests] = useState([]);

  useEffect(() => {
    fetchFriendRequests();
  }, []);

  const fetchFriendRequests = async () => {
    try {
      const { data } = await axios.get("https://randomuser.me/api/?results=5");
      setFriendRequests(data.results);
      setLoading(false);
    } catch (error) {
      console.log(error);
    }
  };
    //  Remove Friend Request Function
    const removefriendRequest = (id) => {
    const newfriendRequests = friendRequests.filter(
      (friendRequest) => friendRequest.id !== id
    );
    setFriendRequests(newfriendRequests);
  };

  if (loading) {
    return (
      <div className="loading">
        <h1>loading...</h1>
      </div>
    );
  }

  return (
    <section>
      <div className="title">
        <h2>friend requests</h2>
        <div className="underline"></div>
      </div>
      <div>
        {friendRequests.map((request, index) => {
          const { login } = request;
          return (
            <Request
              index={index}
              key={login.uuid}
              {...request}
              removefriendRequest={removefriendRequest}   /*{Add as prop}*/
            />
          );
        })}
      </div>
    </section>
  );
};

export default Requests;

Enter fullscreen mode Exit fullscreen mode

Todos os testes passaram!

Imagem em que mostra que os testes passaram

A aplicação parece boa e o botão de recusar funciona conforme o esperado.

Botão Atualizar

Por fim, adicionamos um botão de atualização que carrega novas solicitações de amizade quando o usuário recusou todas as solicitações de amizade. Acesse src/components/Requests/Requests.test.js e adicione o novo bloco de teste abaixo ao bloco describe:

test("Refresh Button loads new friend requests", async () => {
    // declare empty value
    const value = {
      data: {
        results: [],
      },
    };

    // mock axios.get to return an empty array
    axios.get.mockResolvedValueOnce(value);

    render(<Requests />);
    await Promise.all([
      waitForElementToBeRemoved(() => screen.queryByText(/Loading.../i)),
    ]);

    const refreshButton = screen.getByText(/refresh/i);

    fireEvent.click(refreshButton);

    expect(screen.getByText(/friend requests/i)).toBeInTheDocument();
  });

Enter fullscreen mode Exit fullscreen mode

Quando não há mais solicitações de amizade, isso significa que o estado friendRequests no componente <Requests /> é um array vazio; então a primeira coisa que fazemos no bloco de teste acima é simular axios.get para retornar um array vazio. Em seguida, renderizamos nosso componente e esperamos que Loading... seja removido do DOM.

Por fim, o refreshButton é consultado no DOM, um evento de clique é acionado e, em seguida, esperamos que as solicitações de texto de amizade estejam no DOM.

No momento, nosso teste está falhando, então vamos atualizar o componente <Requests />. Fique atento aos comentários abaixo:

import React, { useState, useEffect } from "react";
import Request from "../Request/Request";
import axios from "axios";

const Requests = () => {
  const [loading, setLoading] = useState(true);
  const [friendRequests, setFriendRequests] = useState([]);

  useEffect(() => {
    fetchFriendRequests();
  }, []);

  const fetchFriendRequests = async () => {
    try {
      const { data } = await axios.get("https://randomuser.me/api/?results=5");
      setFriendRequests(data.results);
      setLoading(false);
    } catch (error) {
      console.log(error);
    }
  };

  const removefriendRequest = (id) => {
    const newfriendRequests = friendRequests.filter(
      (friendRequest) => friendRequest.id !== id
    );
    setFriendRequests(newfriendRequests);
  };

  if (loading) {
    return (
      <div className="loading">
        <h1>loading...</h1>
      </div>
    );
  }

  // Condition for when friendRequests is an empty array
  if (friendRequests.length === 0) {
    return (
      <main>
        <div className="title">
          <h2>no Requests</h2>
          <button className="btn" onClick={() => fetchFriendRequests()}>
            refresh
          </button>
        </div>
      </main>
    );
  }

  return (
    <section>
      <div className="title">
        <h2>friend requests</h2>
        <div className="underline"></div>
      </div>
      <div>
        {friendRequests.map((request, index) => {
          const { login } = request;
          return (
            <Request
              index={index}
              key={login.uuid}
              {...request}
              removefriendRequest={removefriendRequest}
            />
          );
        })}
      </div>
    </section>
  );
};

export default Requests;

Enter fullscreen mode Exit fullscreen mode

Agora que atualizamos o componente, espera-se que o teste passe, mas não passou. Temos um erro "não envolvido no ato".

Imagem dos teste falhando

Encontramos esse erro porque fireEvent.click(refreshButton) acionou uma chamada assíncrona. Você pode encontrar esse erro por vários outros motivos ao escrever testes.

Para resolver nosso erro atual, use waitFor para aguardar a conclusão completa da atualização do componente. O waitFor é uma API da biblioteca de testes do React, que permite esperar que as asserções agrupadas passem dentro de uma janela de tempo limite específica.

Vamos atualizar src/components/Requests/Requests.test.js.

Importe waitFor de @testing-library/react:

// src/components/Requests/Requests.test.js

import {
  render,
  screen,
  fireEvent,
  waitFor, // import waitFor
  waitForElementToBeRemoved,
} from "@testing-library/react";

Enter fullscreen mode Exit fullscreen mode

Atualize o teste do botão de atualização com o bloco de código abaixo:

  // Refresh button Test
  test("Refresh Button loads new friend requests", async () => {
    const value = {
      data: {
        results: [],
      },
    };
    axios.get.mockResolvedValueOnce(value);

    render(<Requests />);
    await Promise.all([
      waitForElementToBeRemoved(() => screen.queryByText(/Loading.../i)),
    ]);

    const refreshButton = screen.getByText(/refresh/i);

    fireEvent.click(refreshButton);

    // Wrap assertion within waitFor
    await waitFor(() => {
      expect(screen.getByText(/friend requests/i)).toBeInTheDocument();
    });
  });

Enter fullscreen mode Exit fullscreen mode

Você pode realizar um teste manual verificando como o projeto é executado no navegador.

Além disso, verifique se em seu arquivo package.json você configurou isso:

  "jest": {
    "resetMocks": false 
  },

Enter fullscreen mode Exit fullscreen mode

Conclusão

A importância dos testes de aplicações, especialmente para aplicações grandes, não podem ser exagerados. Escrever testes automatizados é essencial para desenvolver aplicações de alta qualidade. É simples começar a testar aplicações React e aumentar a qualidade do código com a documentação de testes do CRA, React Testing Library e Jest.

Obrigado por ler!

Top comments (0)