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)