Como escrever testes unitários usando React Recoil

Abr 03, 2024

Foto de Ilo Frey no Pexels.

O React Recoil é uma biblioteca de gerenciamento de estado que fornece uma maneira de compartilhar estados entre componentes React. É uma ferramenta desenvolvido e mantida pela equipe do Facebook.

O Recoil tem como principios a simplicidade e a escalabilidade. Os átomos são a unidade básica de estado no Recoil. Eles são gerenciados por um estado global e podem ser lidos e escritos por qualquer componente React. Os seletores são funções puras que aceitam átomos como argumentos e retornam valores derivados.

É muito comum utilizar seletores para acessar o estado global e derivar novos valores mais complexos a partir dos átomos. E é justamente dessas derivações que é comum surgirem bugs 😩. Por isso, é muito importante escrever testes unitários para garantir que os seletores estão escritos de acordo com os requisitos.

Neste artigo, vamos aprender como escrever testes unitários para átomos e seletores do React Recoil.

Show me the code

Para o nosso exemplo prático vamos considerar uma aplicação de controle de placar de um jogo de futebol ⚽️. Vamos criar um átomo para armazenar o placar e um seletor para calcular o total de goals do jogo.

Primeiro, vamos criar o átomo para armazenar o placar do jogo. Sua interface contém um campo para goals do time da casa e um campo para goals do time visitante:

// scoreboard-state.ts import { atom, selector } from "recoil"; interface ScoreBoard { home: number; away: number; } export const scoreBoardState = atom<ScoreBoard>({ key: "score-board", default: { home: 0, away: 0 }, });

Agora, vamos criar o seletor para calcular o total de goals do jogo. O resultado é simplesmente a soma dos goals do time da casa e do time visitante:

// scoreboard-state.ts // (...) export const totalGoalsSelector = selector<number>({ key: "total-goals", get: ({ get }) => { const { home, away } = get(scoreBoardState); return home + away; }, });

Agora, vamos escrever os testes unitários para o átomo e o seletor. Para isso, vamos utilizar a biblioteca Jest.

Primeiro, vamos escrever o teste para o átomo. O teste deve garantir que o átomo foi criado corretamente e que o valor inicial é zero para ambos os times:

// scoreboard-state.spec.ts import { snapshot_UNSTABLE } from "recoil"; import { scoreBoardState, totalGoalsSelector } from "./scoreboard-state"; describe("given a new scoreboard", () => { it("home and away teams should have no goals yet", () => { // Given const snapshot = snapshot_UNSTABLE(); // When const scoreBoard = snapshot.getLoadable(scoreBoardState).valueOrThrow(); // Then const expectedScoreBoard = { home: 0, away: 0 }; expect(scoreBoard).toStrictEqual(expectedScoreBoard); }); });

Seguindo a documentação do Recoil , podemos usar snapshot_UNSTABLE para ler o snapshot do estado atual do Recoil. Em seguida, usamos snapshot.getLoadable(scoreBoardState).valueOrThrow(); para obter o valor do átomo (que até então é o valor default porque não houve mutação de estados). Por fim, comparamos o valor do átomo com o valor esperado.

Agora, vamos escrever o teste para o seletor. O teste deve garantir que o seletor foi criado corretamente e que o total de goals é zero:

// scoreboard-state.spec.ts describe("given a new scoreboard", () => { it("total goals should be 0", () => { // Given const snapshot = snapshot_UNSTABLE(); // When const goals = snapshot.getLoadable(totalGoalsSelector).valueOrThrow(); // Then const expectedGoals = 0; expect(goals).toBe(expectedGoals); }); });

Para este caso, bem semelhante ao exemplo anterior, porém passamos o seletor no método snapshot.getLoadable e comparamos o valor retornado com o valor esperado. Bem simples, não? 😄

Para um caso de teste mais complexo, podemos simular uma mutação de estado e testar se o seletor está retornando o valor correto. Por exemplo, podemos testar se o seletor está somando corretamente os goals dos times:

// scoreboard-state.spec.ts describe("given a scoreboard with home: 3, away: 2", () => { it("total goals should be 5", () => { // Given const inProgressScoreBoard = { home: 3, away: 2 }; const snapshot = snapshot_UNSTABLE(({ set }) => set(scoreBoardState, inProgressScoreBoard) ); // When const goals = snapshot.getLoadable(totalGoalsSelector).valueOrThrow(); // Then const expectedGoals = 5; expect(goals).toBe(expectedGoals); }); });

Para simular uma mutação de estado, passamos uma função para snapshot_UNSTABLE que recebe um argumento set e chama set(scoreBoardState, inProgressScoreBoard) para definir o valor do átomo. Em seguida, obtemos o valor do seletor e comparamos com o valor esperado.

E é isso! Agora você sabe como escrever testes unitários para átomos e seletores do React Recoil. Espero que este artigo tenha sido útil para você e que você evolua a qualidade de seu código escrevendo mais testes! 🚀