DEV Community

Adriana Ferreira Lima Shikasho
Adriana Ferreira Lima Shikasho

Posted on • Updated on

[pt-BR] Como Testar Hooks de uma Aplicação em React - #3

Testes de hooks é algo que assusta muita gente, mas vamos tornar tudo mais simples a partir de agora!

Vamos continuar implementando testes na nossa aplicação de carrinho de ecommerce. Caso você não tenha visto o início dessa parte mais prática, confira na parte 2 deste material.

O que será abordado:

  • Testando Hooks
  • Simulação e mocks
  • Testando ações
  • Testando um Componente que utiliza um Hook

Testando Hooks

O hook deste carrinho possui um contexto que nos fornece as ações de listar produtos, adicionar e remover os itens do carrinho e calcular o valor total dos itens.

A ideia é criar pelo menos 1 teste para cada ação, e utilizar os estados fornecidos pelo contexto para adicionar os mocks. Vamos ver o código do contexto (Vou deixar comentado em código o que cada parte faz):

[...]

function CartProvider({ children }) {

// Estado que armazena os produtos
const [products, setProducts] = useState<CartItem[]>(() => {
    // pega os items em local storage
    const itemsStorage = localStorage.getItem('@UnitTest:cart');
    // se tiver items, retorna o valor como estado inicial
    if (itemsStorage) return JSON.parse(itemsStorage);
    // Se não tiver, retorna e inicia vazio.
    return [];
  });

  // Atualiza Local Storage com um novo array de itens
  useEffect(() => {
     localStorage.setItem('@UnitTest:cart', 
     JSON.stringify(products: CartItem[]));
  }, [products]);

// Ação que adiciona um item no carrinho
 const addToCart = useCallback(
     // Verifica se esse produto já existe no carrinho
     (item: Product) => {
     const productReceived = products.find(
        product => product.id === item.id
     );

     // Se sim, adiciona 1 na contagem do item existente
     if (!!productReceived) {
       productReceived.count += 1;
       setProducts([...products]);
     }

     // Se não, inclui o item no array com 1 no contador.
     else {
       setProducts([...products, { ...item, count: 1 }]);
     }
    },
 [products],
 );

[...]
Enter fullscreen mode Exit fullscreen mode

Simulações e mocks

Certo! Agora, vamos implementar o primeiro teste deste hook que será a funcionalidade de pegar os itens que estão salvos em Local Storage.

Lembrando que esses items são armazenados no estado products, então o que esperamos neste teste, é que o e estado products tenha um array com algum item adicionado.

Esse teste vai se chamar getItems from LocalStorage. O código abaixo descreve esse teste, e você pode perceber que ele segue o mesmo padrão de escrita de testes que já aprendemos.

import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { waitFor, act } from '@testing-library/react';

import { CartProvider, useCart } from '.';

describe('Cart hook tests', () => {
  test('get items from localStorage', async () => {

    // 1. renderizar o hook
    const { result } = renderHook(() => useCart(), {
      wrapper: CartProvider
    });

    // 2. fazer uma query ou mock
    const itemAddedToCart = {
      id: 1,
      createdAt: 'some_value',
      name: 'Product Test',
      price: 90,
      image: 'image_path',
      stock: 9,
    };

    // 3. executar alguma ação
    jest.spyOn(Storage.prototype,'getItem')
    .mockImplementation(key => {
      switch (key) {
        case '@ReactUnitTest:cart':
          return JSON.stringify([itemAddedToCart]);
        default:
          return null;
      }
    });

    // 4. descrever o resultado esperado
    await waitFor(() => {
      expect(result.current.products[0].name)
      .toEqual('Product Test');
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

renderHook: como hook não é um componente, utilizamos esse método para renderizar o Hook e conseguir pegar o resultado dele. Neste caso, importamos o useCart e o envolvemos pelo seu provider o CartProvider.

result: pega o resultado de alguma ação ou estado que veio do contexto. No caso, pegar o estado products.

jest.spyOn: faz a simulação de alguma funcionalidade, neste caso simulamos o Local Storage.

mockImplementation: implementa um mock na simulação, neste caso, implementamos o mock de item na simulação de LocalStorage

waitFor: espera por um resultado.

expect: descreve o resultado esperado, aqui nós esperamos que o resultado, ou seja, que o array products tenha um item com o nome igual ao que passamos no mock.

Testando ações

Nosso próximo teste do hook será sobre adicionar items no carrinho. Queremos garantir que a function addToCart está funcionando corretamente.

O início será bem parecido com o teste anterior, da qual vamos renderizar o hook e criar um mock do item. O resultado esperado também será o mesmo, o que vai mudar aqui vai ser justamente o fato de que queremos testar uma açao do hook. Vamos ver como fica:

test('get items from localStorage', async () => {

    // 1. renderizar o hook
    const { result } = renderHook(() => useCart(), {
      wrapper: CartProvider
    });

    // 2. fazer uma query ou mock
    const itemAddedToCart = {
      id: 1,
      createdAt: 'some_value',
      name: 'Product Test',
      price: 90,
      image: 'image_path',
      stock: 9,
    };

    // 3. executar alguma ação
    act(() => {
      result.current.addToCart(itemAddedToCart);
    });

    // 4. descrever o resultado esperado
    await waitFor(() => {
      expect(result.current.products[0].name)
      .toEqual('Product Test');
    });
  });
Enter fullscreen mode Exit fullscreen mode

act: faz com que a renderização seja o mais próximo possível do que acontece no Browser. Neste caso, chamamos no método act a ação que queremos testar e passamos pra ela o mock como parâmetro.

expect: se tudo ocorrer bem, o resultado também será um array products com 1 item adicionado.

O hook ainda tem duas ações para ser testadas: removeToCart e getTotalPrice. Entretanto, fica como desafio, pois da pra fazer baseado naquilo que já vimos aqui.

Testando um Componente que utiliza um Hook

Ok, testamos duas funcionalidades do hook useCart e agora vamos testar um componente que utiliza esse hook, o próprio carrinho de compras. Como sempre, vamos olhar o código do componente:

import React from 'react';

import { ReactComponent as CartIcon } from '../../../assets/cart.svg';
import { useCart } from '../../../hooks/Cart';
import { Container } from './styles';

export default function Cart() {
  const { products } = useCart();

  return (
    <Container to="/cart" data-testid="cart_link_component">
      <CartIcon />
      {products.length > 0 && (
        <div>
          <span>{products.length}</span>
        </div>
      )}
    </Container>
  );
};
Enter fullscreen mode Exit fullscreen mode

Você se lembra qual o primeiro teste que costumamos escrever quando testamos um componente? Isso mesmo, testamos se ele está sendo exibido corretamente na tela.

Entretanto, como esse componente utiliza o hook useCart, não podemos simplesmente testar como já fizemos nos anteriores. Aqui nós precisaremos mockar o hook, já que o componente depende dele.

Vamos utilizar o mesmo método que usamos para simular o LocalStorage, o jest.spyOn. E para mockar também o retorno do hook, vamos passar um objeto pelo método mockReturnValue com o estado products e as ações que o hook disponibiliza.

import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { render } from '@testing-library/react';
import * as hookCart from '../../../hooks/Cart';

describe('Cart component tests', () => {
  test('renders without crashing', () => {
    jest.spyOn(hookCart, 'useCart').mockReturnValue({
      products: [],
      addToCart: jest.fn(),
      removeToCart: jest.fn(),
      getTotalPrice: 0,
    });

    const { getByTestId } = render(<Cart />, {
      wrapper: BrowserRouter
    });

   expect(getByTestId('cart_link_component')).toBeInTheDocument();
  });
}
Enter fullscreen mode Exit fullscreen mode

import *: vamos importar tudo que vem do hook para simular

jest.spyOn: passando o que foi importado, vamos simular o hook useCart e mockar no mockReturnValue seus valores.

expect: utilizamos o mesmo método anterior de getByTestId e esperamos que o componente esteja na tela.

👉 Veja como testar consumo de API na parte 4


Referências:

Esse conteúdo foi baseado no vídeo “Treinamento: Como implementar testes unitários em React utilizando Jest” do canal da Zappts, feito pelo Cláudio Castro.

Confira o repositório com o projeto: https://github.com/ccastrojr/react_unit_test

Latest comments (0)