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], ); [...]
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'); }); }); });
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'); }); });
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> ); };
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(); }); }
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
Top comments (0)