DEV Community

Genilson fernandes
Genilson fernandes

Posted on

3 2 2 3 3

Como testar hooks com vitest

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.

aqui o exemplo de uso

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;

Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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([]);
  });
Enter fullscreen mode Exit fullscreen mode

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 o expect, 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([]);
  });
Enter fullscreen mode Exit fullscreen mode

É 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.

coverage

Assim ficou a coverage desse hook, apesar de simples, é importante testar essas pequenas abstrações do código, é isso hoje.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (1)

Collapse
 
matheusmangueira profile image
Matheus Mangueira

Muito bom, estava precisando de um artigo desse. Me ajudou bastante!!

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up