Testes baseados em propriedades em TypeScript: como capturar bugs que você não procurou

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que os testes unitários não salvam de tudo

\n\n

Imagine que você escreveu uma função de ordenação de array. Você a cobriu com uma dúzia de testes unitários: array vazio, um elemento, lista já ordenada, ordem inversa. Os testes estão verdes. Você confia no código. Mas uma semana depois, a produção cai — em um array de 1000 elementos com duplicatas e números negativos. Familiar?

\n\n

O teste TypeScript tradicional baseado em exemplos (example-based testing) verifica apenas os cenários que você imaginou. E se o bug estiver escondido em uma combinação que você não considerou? É aqui que entra o teste baseado em propriedades (property-based testing) — uma abordagem que gera automaticamente milhares de entradas aleatórias e verifica os invariantes do seu código.

\n\n

Neste artigo, vamos explorar como aplicar testes baseados em propriedades em TypeScript com a biblioteca fast-check, quais propriedades verificar e como isso muda a abordagem de desenvolvimento.

\n\n

O que é teste baseado em propriedades?

\n\n

Em vez de escrever:

\n
it('ordena [3, 1, 2] → [1, 2, 3]', () => {\n  expect(sort([3, 1, 2])).toEqual([1, 2, 3]);\n});\n
\n\n

Você descreve uma propriedade — uma afirmação que deve ser verdadeira para todas as entradas válidas:

\n
it('array ordenado não altera a ordem dos elementos', () => {\n  fc.assert(\n    fc.property(fc.array(fc.integer()), (arr) => {\n      const sorted = sort(arr);\n      // Propriedade 1: o comprimento é preservado\n      expect(sorted.length).toBe(arr.length);\n      // Propriedade 2: cada elemento do array original está presente\n      // Propriedade 3: o array está ordenado em ordem crescente\n      for (let i = 1; i < sorted.length; i++) {\n        expect(sorted[i - 1]).toBeLessThanOrEqual(sorted[i]);\n      }\n    })\n  );\n});\n
\n\n

A biblioteca fast-check gera centenas de arrays aleatórios (incluindo vazios, com NaN, com números decimais, com valores enormes) e executa seu teste em cada um. Se algum falhar — você descobre o problema.

\n\n

Por que isso é necessário especificamente em TypeScript?

\n\n

TypeScript oferece tipagem estática, mas ela não garante a correção da lógica. O teste baseado em propriedades em TypeScript preenche a lacuna entre "os tipos coincidem" e "o código funciona corretamente". Isso é especialmente útil para:

\n\n
    \n
  • Funções de transformação de dados (map, filter, reduce)
  • \n
  • Parsers e validadores
  • \n
  • Algoritmos com lógica não trivial
  • \n
  • Funções que trabalham com valores limite
  • \n
\n\n

Biblioteca fast-check: início rápido

\n\n

Instale o fast-check no projeto:

\n
npm install --save-dev fast-check\n
\n\n

Sintaxe básica:

\n
import fc from 'fast-check';\n\ndescribe('propriedades da função reverse', () => {\n  it('reverse(reverse(arr)) === arr', () => {\n    fc.assert(\n      fc.property(fc.array(fc.anything()), (arr) => {\n        expect(reverse(reverse(arr))).toEqual(arr);\n      })\n    );\n  });\n});\n
\n\n

Aqui, fc.array(fc.anything()) é um gerador de arrays arbitrários. Você pode combinar geradores:

\n
    \n
  • fc.integer() — números inteiros
  • \n
  • fc.string() — strings
  • \n
  • fc.record({ name: fc.string(), age: fc.integer({ min: 0, max: 120 }) }) — objetos
  • \n
  • fc.oneof(...) — escolha aleatória entre vários geradores
  • \n
\n\n

Quais propriedades verificar? 5 exemplos práticos

\n\n

1. Invariância (reversibilidade)

\n

Qualquer operação que tenha uma inversa deve ser reversível:

\n
it('serialização e desserialização JSON', () => {\n  fc.assert(\n    fc.property(fc.object(), (obj) => {\n      const json = JSON.stringify(obj);\n      const parsed = JSON.parse(json);\n      expect(parsed).toEqual(obj);\n    })\n  );\n});\n<                

Blogs

Book Recommendations