QuickCheck
Quando desenvolvemos software, é imprescindível que o testemos para verificar a corretude de nosso código, ou pelo menos ganhar confiança em sua corretude. Executar baterias de testes manualmente a cada modificação é um processo tedioso e que certamente levará a testes não serem executados. Além disso, à medida que nossas funções ficam mais complexas, gastaríamos mais e mais tempo definindo e executando testes. Uma ferramenta que nos ajuda a fazer nestas tarefas é a biblioteca QuickCheck. Para entender como usar a QuickCheck, considere as duas seguintes funções.
celsius2fahrenheit c = c * 9 / 5 + 32
fahrenheit2celsius f = (f - 32) * 5/9
Elas são obviamente inversas uma da outra, o que quer dizer que se eu aplicar um valor qualquer à primeira função e então aplicar o resultado na segunda, eu deveria recuperar o valor original.
Por exemplo, se o valor qualquer é 100, 100 == fahrenheit2celsius (celsius2fahrenheit 100)
.
Pois a QuickCheck nos permite escrever exatamente este tipo de afirmação e tê-la testada automaticamente para um conjunto de valores aleatórios.
Obviamente, mesmo se o código passar nos testes gerados pelo QuickCheck, não quer dizer que outros valores não resultariam em erros.
Contudo, nossa confiança na corretude aumentaria.
Para testar as funções acima escreveríamos então a propriedade como uma função que retorna um booleano.
A propriedade tem um nome iniciado em prop_
, mas isso é apenas uma convenção.
A definição recebe como parâmetro um valor c
, converte para Celsius e de volta para Fahrenheit, e confere se o valor resultante é igual ao valor de entrada.
Observe que o resultado da função é o resultado da comparação com o valor inicial, o que é equivalente mas muito mais limpo do que fazer um teste do tipo if Condição then True else False
.
prop_C2f2C c = fahrenheit2celsius (celsius2fahrenheit c) == c
Para usar a biblioteca, precisamos importá-la, no início do arquivo, com um import Test.QuickCheck
.
Assim, o arquivo ficará assim.
import Test.QuickCheck
celsius2fahrenheit c = c * 9 / 5 + 32
fahrenheit2celsius f = (f - 32) * 5/9
prop_C2f2C c = fahrenheit2celsius (celsius2fahrenheit c) == c
Para testar o código, execute o ghci carregando o QuickCheck. A forma mais simples de fazê-lo é usando stack: stack ghci --package QuickCheck
.
Agora carregue seu programa, usando :l f2c2f
no meu caso e execute o teste
> quickCheck prop_C2f2C
*** Failed! Falsified (after 2 tests and 4 shrinks):
-0.1
Ooops! O teste falhou para o valor -0.1. Mas por quê? Vejamos cada função isoladamente.
> celsius2fahrenheit (-0.1)
31.82
> fahrenheit2celsius 31.82
-9.999999999999984e-2
Os valores são próximos, mas não são iguais, por causa de problemas de arredondamento causados pela imprecisão dos tipos utilizados. O problema está no teste, pois é impossível para o computador representar certos valores e estes erros de aproximação ocorrerão. Assim, uma estratégia melhor é definir um operador quase igual, para comparar valores com pequenos erros.
import Test.QuickCheck
celsius2fahrenheit c = c * 9 / 5 + 32
fahrenheit2celsius f = (f - 32) * 5/9
prop_C2f2C c = fahrenheit2celsius (celsius2fahrenheit c) ~== c
l ~== r = abs (l - r) < ε
where ε = 10e-10
Com esta nova definição, nosso teste agora passa com sucesso.
> quickCheck prop_C2f2C
+++ OK, passed 100 tests.