E se os testes se escrevessem sozinhos? Conhecendo os testes baseados em propriedades
\n\nVocê já escreveu um teste unitário, executou-o, recebeu a marca verde e, um mês depois, encontrou um bug na mesma função? Familiar? Os testes unitários clássicos são como verificar se sua carteira não está vazia, olhando apenas para uma nota. Você verifica um cenário específico, um conjunto específico de dados de entrada. E se eu lhe dissesse que existe uma maneira de fazer o computador inventar milhares de cenários diferentes e verificar se seu código não quebra?
\n\nIsso não é ficção científica. São os testes baseados em propriedades em JavaScript (ou teste baseado em propriedades). Em vez de escrever "se passar 2 e 3, obtemos 5", você descreve uma propriedade da sua função: "para quaisquer dois números, o resultado da soma será um número que é sempre maior ou igual a cada uma das parcelas". E o ambiente de teste gera centenas de pares aleatórios de números, verificando essa afirmação.
\n\nNeste artigo, vamos explorar como funciona o teste baseado em propriedades, como ele difere dos testes comuns e como implementá-lo em seu projeto usando a excelente biblioteca fast-check. Você verá como é fácil encontrar bugs "de fronteira" que você nem pensaria em procurar.
Por que os testes unitários comuns nem sempre salvam?
\n\nVamos direto ao ponto. Suponha que você tenha uma função que ordena um array de números em ordem crescente:
\n\nfunction sortNumbers(arr) {\n return arr.slice().sort((a, b) => a - b);\n}\n\nVocê escreve um teste unitário:
\n\ntest('ordena o array [3, 1, 2]', () => {\n expect(sortNumbers([3, 1, 2])).toEqual([1, 2, 3]);\n});\n\nO teste está verde. Você está feliz. Mas e se o array tiver null? Ou undefined? Ou números muito grandes? Ou um array com 10.000 elementos? Você não vai escrever um teste para cada um desses casos. Mas o teste baseado em propriedades em JavaScript vai.
Em vez de listar exemplos, você descreve invariantes (propriedades) que devem ser sempre verdadeiras:
\n- \n
- O comprimento do array ordenado é igual ao comprimento do array original. \n
- Cada elemento do array original está presente no array ordenado. \n
- Para qualquer índice
i, é verdade quearr[i] <= arr[i+1]. \n
São exatamente essas propriedades que o teste de propriedades verificará.
\n\nBiblioteca fast-check: sua ajudante em testes aleatórios
\n\nNo mundo JavaScript, a principal ferramenta para testes baseados em propriedades é a fast-check. Ela é capaz de gerar dados aleatórios de qualquer tipo: números, strings, arrays, objetos, datas — e até estruturas complexas.
\n\nA instalação é simples:
\nnpm install fast-check --save-dev\n\nAgora vamos escrever nosso primeiro teste baseado em propriedades para a função sortNumbers. Usaremos o framework Jest, mas o fast-check funciona com qualquer executor de testes.
const fc = require('fast-check');\n\ntest('a ordenação preserva o comprimento do array', () => {\n fc.assert(\n fc.property(fc.array(fc.integer()), (arr) => {\n const sorted = sortNumbers(arr);\n return sorted.length === arr.length;\n })\n );\n});\n\nO que está acontecendo aqui? fc.array(fc.integer()) é um gerador que cria arrays aleatórios de inteiros aleatórios. fc.property pega esse gerador e verifica a afirmação (nossa função arrow) em 100 (por padrão) conjuntos diferentes de dados. Se pelo menos um teste falhar — fc.assert lançará um erro e mostrará um contraexemplo.
Adicionando mais propriedades
\n\nUma propriedade é bom, mas três é melhor. Vamos verificar se, após a ordenação, o array está realmente ordenado:
\n\ntest('a ordenação retorna um array ordenado', () =>