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.

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (1)

Collapse
 
matheusmangueira profile image
Matheus Mangueira

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

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay