O uso de hooks no React é uma prática valiosa para abstrair partes do nosso código, seja para realizar chamadas a APIs, lidar com cálculos complexos ou outras tarefas. entre tanto os hooks sejam extremamente úteis, é importante garantir que funcionem corretamente, especialmente em um ambiente controlado onde podemos definir a entrada e controlar a saída.
Abaixo criei um hook super simples que gerencia um estado de uma lista, algo comum no dia a dia como deve, nesse exemplo, vou mostrar a implementação e como testar, e as partes que estão sendo testadas.
Esse aqui é o hook
import { useState } from 'react';
const useList = <T>(initialList: T[]) => {
const [list, setList] = useState<T[]>(initialList);
const onToggle = (id: T) => {
if (list.includes(id)) {
setList(list.filter((item) => item !== id));
} else {
setList([...list, id]);
}
};
const onSelectAll = (ids: T[]) => {
setList(ids);
};
const isSelected = (id: T) => {
return list.includes(id);
};
const onClear = () => {
setList([]);
};
return { list, onToggle, onSelectAll, onClear, isSelected };
};
export default useList;
Como pode ver, ele retorna uma lista de ids em um array, e tem os métodos para controlar a lista: onSelectAll, isSelected, onClear e list.
Assim fica a chamada do hook
const {
list,
onSelectAll,
onClear,
onToggle } = useList<number>([1, 2]);
Mas vamos os testes….
Primeiro de tudo, não vou ensinar como configura o ambiente de teste, não é difícil de fazer, com o vitest é quase automático.
Detalhes, antes de começar, o testing-library oferece dois métodos para gente testar hooks: act, renderHook, sendo o ACT, para lidar com atualizações de estado que causa efeito colaterais e o renderHook, como nome já diz, ele renderizar nosso hook em diferentes situações
it('should initialize with provided initial list', () => {
const initialList = [1, 2, 3];
const { result } = renderHook(() => useList<number>(initialList));
expect(result.current.list).toEqual(initialList);
});
it('should toggle items in the list', () => {
const { result } = renderHook(() => useList<number>([1, 2, 3]));
act(() => {
result.current.onToggle(2);
});
expect(result.current.list).toEqual([1, 3]);
act(() => {
result.current.onToggle(4);
});
expect(result.current.list).toEqual([1, 3, 4]);
});
it('should toggle all items in the list', () => {
const { result } = renderHook(() => useList<number>([1, 2, 3]));
act(() => {
result.current.onSelectAll([1, 2, 3, 4]);
});
expect(result.current.list).toEqual([1, 2, 3, 4]);
act(() => {
result.current.onClear();
});
expect(result.current.list).toEqual([]);
});
Como gosto de fazer, é que o teste seja autoexplicativo, trazendo consistência, seja para mim ou para quem vai ver o código e fazer modificações.
- No primeiro caso, estamos testando se o hook foi iniciado corretamente, sem novidades até aqui.
- No segundo caso, vamos iniciar o hook com uma lista de 3 itens e, em seguida, desmarcar o item 2. Como pode ser visto, essas ações que vão afetar o estado estão envoltas em um
act
, e em seguida oexpect
, que nesse caso, esperamos uma lista sem o id 2. - No terceiro caso, vamos marcar todos os itens e em seguida testar o terceiro método junto, que é o
onClear
. Como pode ser visto, esperamos um array vazio.
it('should check if an item is selected', () => {
const { result } = renderHook(() => useList<number>([1, 2, 3]));
expect(result.current.isSelected(2)).toBe(true);
expect(result.current.isSelected(4)).toBe(false);
});
it('should clear the list', () => {
const { result } = renderHook(() => useList([1, 2, 3]));
act(() => {
result.current.onClear();
});
expect(result.current.list).toEqual([]);
});
É comum acabarmos testando algo duas vezes, e neste caso, o onClear
já tinha sido testado no caso 3. No entanto, para manter uma consistência e garantir que cada caso tenha seu próprio teste, vamos repeti-lo no caso 5.
- Caso 4: Este caso testa a função que verifica se um item existe na lista. Veja que estamos fazendo algo novo aqui, que é primeiro testar o valor esperado e, em seguida, testar também um valor que não estará na lista. Isso garante a cobertura de ambos os casos, sendo importante nesta função.
- Caso 5: Como mencionado, este caso já foi testado acima, então vou economizar palavras.
Assim ficou a coverage desse hook, apesar de simples, é importante testar essas pequenas abstrações do código, é isso hoje.
Top comments (1)
Muito bom, estava precisando de um artigo desse. Me ajudou bastante!!