Testes de propriedade conduzidos por gerador, por que e como

Por quê:

Quando seu código é executado no mundo real, você não conhece as entradas de uma função. Os valores vêm da pilha de chamadas ou vêm de fontes externas, como entrada do usuário ou sistemas remotos. Caso contrário, seu software é um sistema completamente fechado e você pode usar um assistente de prova em vez de uma linguagem de programação de propósito geral.

Supondo que esse não seja o seu cenário, por que, então, você testa seu software como se conhecesse as entradas? Os testes que passam nas entradas conhecidas e afirmam as saídas são completamente anêmicos. Qual é o tipo de sua entrada? Uma linha? Um número? Algum produto contendo uma mistura de outros tipos? Quantos valores podem habitar esse tipo? Centenas? Milhares? Infinito? Provavelmente sim.

Então, o que suas cinco a vinte afirmações contra os resultados de suas cinco a vinte entradas conhecidas escolhidas a dedo realmente dizem sobre seu código? Nada significativo, mas você pode dizer que tentou.

Se você deseja uma garantia significativa de que seu software está correto, teste-o da forma como é chamado, com entradas desconhecidas. Se você não fez isso antes (e mesmo se você fez), não é imediatamente óbvio como fazer isso e é ainda mais desafiador de fazer sem usar a abordagem antiga ou reimplementar o código em teste para alcançar uma linha de chegada que equivale a afirmar a verdade.

Como:

O código de teste com entradas desconhecidas é exclusivo do código em teste e requer a identificação de propriedades observáveis ​​desse código, que são mais amplas em escopo do que a variedade típica “dê x, retorna y”. É por isso que o teste acionado por gerador (as entradas são geradas aleatoriamente em vez de codificadas) aparentemente sempre é pareado com o teste de propriedade (a definição dada na frase anterior).

Apesar das demandas que variam com base no código em teste, há uma fórmula que geralmente produz garantias altamente eficazes, incidentalmente revelando propriedades significativas do código em teste ao longo do caminho. É estupidamente simples:

  1. Passe as entradas para a função em teste.
  2. Observe a saída.
  3. Assegure as entradas com base na saída.

A chave é que, em vez de tentar saber coisas sobre o resultado com base na entrada (como normalmente encontrado em testes baseados em entradas conhecidas estaticamente), você inverte o relacionamento.

As asserções aqui irão emparelhar o que aconteceu (o estado do resultado dado pela função em teste) com o motivo (os aspectos das entradas que podem ser deduzidos com base nas propriedades do resultado sendo observado).

O resultado final é uma representação bastante precisa das propriedades observáveis ​​de uma função em um conjunto significativo de entradas, onde o processo o levará a restringir e / ou liberar as entradas e saídas que a função aceita e produz, respectivamente, de modo a oferecer suporte a definição de tal conjunto de propriedades observáveis. Funções como essa são confiáveis, compreensíveis e seguras para construir.

Em suma, seus testes devem vincular entradas a saídas, observando as saídas como um meio de descrever as entradas, de forma que a descrição da entrada seja uma explicação de por que a saída obtida foi produzida como foi.