O que será abordado:
- Primeiros passos
- Enxergando Padrões
- Escrevendo o primeiro teste de Componente
- Testanto Componentes que possuem dependências
- Testando um evento de usuário no Componente
Primeiros passos
Vamos imaginar uma aplicação bem simples que simula um ecommerce. Nela teremos duas páginas:
Home: que é a loja que lista todos os produtos. Cada item pode ser adicionado no carrinho ao clique de um botão.
Cart: que é o próprio carrinho da qual podemos ver os itens que foram adicionados para compra e excluí-los caso desejado.
Você encontra o repositório com o projeto aqui
Ao longo da aplicação temos alguns botões, e um deles é o que usamos para adicionar algum item no carrinho. Por isso foi criado um componente único de botão. Vamos ver esse código:
// Button.tsx
import React from 'react';
import { Container } from './styles';
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
size: 'default' | 'large';
};
export default Button({ children, ...rest }: ButtonProps) {
return (
<Container {...rest} data-testid="pure_button">
{children}
</Container>
);
};
Para incluir um teste para esse componente, vamos criar na mesma pasta um arquivo Button.spec.tsx
Enxergando Padrões
De maneira didática e para nos ajudar a lembrar como deve ser a estrutura de escrita cada teste, podemos pensar em seguir o seguinte padrão:
- Renderizar o componente passando suas props
- Fazer uma query ou um mock
- Executar alguma ação
- Descrever o resultado esperado
Uma outra dica que podemos manter em mente é sempre pensar que basicamente tudo em testes é uma simulação. Seria como se fosse uma réplica das funcionalidades, só que todos os dados e ações são "mockados" ou simplesmente falsos.
O objetivo é montar o teste de forma que replique de maneira simulada determinada funcionalidade, mas utilizando as ferramentas que a lib de testes provê. Mas vamos aos poucos que daqui a pouco começa a ficar mais claro.
Escrevendo o primeiro teste de Componente
O título desta suite será “Button component tests” (Testes do componente Button), e o primeiro teste, verificaremos se este componente está sendo exibido corretamente na tela. Esse tipo de teste é o mais básico, e pode ser o primeiro teste para qualquer componente.
// Button.spec.tsx
import React from 'react';
import { render } from '@testing-library/react';
import Button from '.';
describe('Button component tests', () => {
test('renders without crashing', () => {
render(<Button size="default" />);
const button = screen.getByTestId('pure_button')
expect(button).toBeInTheDocument();
});
});
Ao enxergar o padrão que explicamos anteriormente, percebemos que neste caso em específico não precisaremos executar nenhuma ação (item 3), mas veremos um exemplo com ação mais pra frente.
[...]
// 1. renderizar o componente passando suas props
render(<Button size="default" />);
// 2. fazer a query do componente
const button = screen.getByTestId('pure_button')
// 4. descrever o resultado esperado
expect(button).toBeInTheDocument();
[...]
describe
: descreve um conjunto de testes que estão associados. O describe por si só não executa teste, ele só agrupa os testes de determinado componente. Ele recebe 2 parâmetros: um texto que descreve a suite de testes, e uma função callback. Nessa função, vamos escrever cada teste.
test ou it
: implementa o teste. Também recebe 2 parâmetros: um texto bem descritivo do teste, e uma função callback com a execução do teste de fato.
render
: usado para identificar qual componente da aplicação que queremos renderizar para testar. Ele precisa receber as props obrigatórias desse compontent. O render retorna vários métodos para fazermos a query desse component. Uma delas é a getByTestId.
getByTestId
: para usar essa função é necessario ter o atributo data-testid no componente que queremos testar. Ele é um atributo único que vai nos ajudar a fazer a query do componente.
expect
: o que a gente espera que aconteça. Neste caso, a gente espera que o botão esteja no documento, ou seja, que seja renderizado corretamente, então utilizamos o método toBeInTheDocument()
.
Testanto componentes que possuem dependências
Beleza, já temos nosso primeiro teste, o mais simples deles, agora vamos começar a nos aprofundar um pouco mais. Lembrando na nossa aplicação sendo um ecommerce, nós temos um Link (chamado MenuOption) no menu superior da home que vai nos redirecionar para a página de carrinho.
import React from 'react';
import { LinkProps } from 'react-router-dom';
import { Container } from './styles';
interface Props extends LinkProps {
label: string;
}
export default function MenuOption({ label, ...rest }:LinkProps ){
return <Container {...rest}>{label}</Container>;
};
MenuOption;
O primeiro teste, será o mesmo que escrevemos para o botão, mas veremos uma outra forma de fazer a query do nosso componente MenuOption.
describe('MenuOption component tests', () => {
test('renders without crashing', () => {
// 1. renderizar o componente passando suas props
render(<MenuOption label="Link Teste" to="/" />);
// 2. fazer a query do componente
const link = screen.getByText('Link Teste')
// 4. descrever o resultado esperado
expect(link).toBeInTheDocument();
});
}
Como podemos ver, fizemos a query utilizando o getByText, em que podemos pegar algum texto que está aparecendo na tela.
Neste caso, o MenuOption exibe um texto que recebe no parâmetro label, entretanto, aqui no teste podemos “mockar” qualquer texto, pois o importante é a query funcionar, e não precisa ser exatamente como na aplicação.
Então vamos passar como parâmetro o label='Link Teste'
e vamos usar essa mesma string na query getByText('Link Teste')
para selecioná-lo.
Não podemos esquecer de passar no render, todos os parâmetros obrigatórios para aquele componente. Por exemplo, o MenuOption sendo um Link
que veio do react-router-dom, ele obrigatoriamente precisa que seja passado um to='/'
, se não dá erro.
Feito isso, escrevemos o resultado esperado, que é que o componete esteja no documento, conforme já vimos.
Ok, agora vamos rodar nosso teste. Executamos yarn test, e... percebemos que não passou! Ué, o que fizemos de errado?
Vamos verificar qual a mensagem de erro que aparece no console:
Invariant failed: You should not use <Link> outside a <Router>
Mas o que isso significa?
Um componente Link para que ele possa ser renderizado, eu preciso ter por volta dele um Router que é o responsável pelo redirecionamento, sem ele, o link não funciona.
Para resolver isso, vamos envolvê-lo passando um segundo parâmetro no método render, que é um objeto com um wrapper. E dentro do wrapper passamos o BrowserRouter
, que é o resposável por criar as rotas e fazer o Link funcionar.
O teste correto vai ficar assim agora:
[...]
// no render passaremos o objeto com wrapper: BrowserRouter
render(<MenuOption label="Link Teste" to="/" />, {
wrapper: BrowserRouter,
});
[...]
Agora sim! Ao rodar os testes, ele vai passar, pois não teremos nenhum problema de dependência para a renderização do componente.
Então ótimo, agora já aprendemos que toda vez que aparecer essa mensagem de erro de que algum componente não pode ser renderizado fora de outro componente, já sabemos que precisamos “envolvê-lo” com esse componente da qual ele tem dependência.
Testando um evento de usuário no Componente
Certo, como prometemos, vamos ver um exemplo que agora contém os 4 pontos que definimos no nosso padrão de escrita lá trás, que é alguma ação.
Nesse mesmo componente MenuOption, lembrando que ele é um Link e todo link serve para navegação, e normalmente isso é feita através de um clique do usuário no link.
Por isso, nosso próximo teste será verificar se o redirecionamento para a próxima página está funcionando ao clique do usuário no link. Vamos escreve-lo, acrescentando o método userEvent.click()
.
o método userEvent, descreve exatamente isso, uma ação ou evento feita pelo usuário e pode ser obtida pela importação do '@testing-library/user-event’
. Vai ficar assim:
test('click to redirect', () => {
// 1. renderizar o componente passando suas props
render(<MenuOption label="Button Test" to="/url" />, {
wrapper: BrowserRouter,
});
// 2. fazer a query do componente
const link = screen.getByText('Link Teste')
// 3. executar alguma ação
userEvent.click(link);
// 4. descrever o resultado esperado
expect(global.window.location.pathname).toEqual('/url');
});
Depois do clique o usuário teremos um resultado esperado propício a um link: queremos que aconteça um redirect para uma outra página. E qual será essa página? Será a string que passamos nas prop to
que definimos no render do MenuOption, neste caso, to='/url'
.
Então lá no expect, vamos usar um método do próprio javascript que pega o pathname da url, o método global.window.location.pathname
, e esperamos que ele seja igual a string que deifinimos na prop, utilizando o método toEqual('/url')
.
👉 Veja como testar hooks na parte 3
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)