Baixe o app para aproveitar ainda mais
Prévia do material em texto
Testes e depuração em Haskell Autor: Lucas Inojosa Data: 14/04/2011 v1.0 Um assunto no qual senti dificuldade em Haskell quando paguei PLC, especialmente no projeto, foi como debugar as funções e testá-las. Em linguagens orientadas a objeto e imperativas, temos o famoso recurso de "imprimir na tela". Em haskell também temos (putStrLn "olá, mundo"), porém é uma função IO e só pode ser chamada por funções IO, e não funciona em funções puras. Aqui vou apresentar um tutorial bem básico sobre isso, também introduzindo a biblioteca HUnit. A função Debug.Trace.trace Haskell nos oferece outro recurso de impressão de resultados na tela em funções puras, a função "trace :: String -> a -> a". Esta função recebe um argumento String, que será impresso na tela, e um argumento de qualquer outro tipo, que simplesmente será retornado no final. Um exemplo: vou definir uma função que eleva um numero a outro. potencia :: Int -> Int -> Int potencia base 0 = 1 potencia base exp = base * resultadoRecursivo where resultadoRecursivo = potencia base exp-1 Vamos dizer que a cada fim de chamada recursiva, eu quero que o resultado da potencia seja impressa na tela. Faríamos assim: import Debug.Trace potencia :: Int -> Int -> Int potencia base 0 = 1 potencia base exp = base * (trace ("o resultado parcial eh: " ++ (show resultadoRecursivo)) resultadoRecursivo) where resultadoRecursivo = potencia base exp-1 Essas 2 funções têm comportamento semântico idênticos, excluindo a ação de E/S de imprimir na tela. Vamos analisar a função trace: trace ("o resultado parcial eh: " ++ (show resultadoRecursivo)) resultadoRecursivo O que ela faz? Simples. O primeiro argumento inteiro (até a função show) é uma string que exibe o resultado parcial recursivo, e o segundo argumento é o próprio resultado em si. A função vai imprimir a primeira string e vai retornar exatamente o resultado recursivo (o segundo argumento). Testem para ver! Com isso, é possível imprimir resultados obtidos dentro de funções puras. Testes unitários com HUnit Quem já pagou Engenharia de Software deve estar familiarizado com testes unitários e a biblioteca JUnit. O HUnit segue a lógica e o padrão dos testes unitários do JUnit, porém é implementada em Haskell. Será bastante útil no projeto. Segue um exemplo: Digamos que nós temos uma função que calcula fatorial, e queremos testar seu comportamento: fatorial :: Int -> Int fatorial 0 = 1 fatorial n | n < 0 = -1 | otherwise = n * fatorial (n-1) Um teste completo da funcionalidade desta função ficaria mais ou menos assim: import Test.HUnit Importado o HUnit, vamos construir alguns casos de testes: teste1 = TestCase (assertEqual "Teste do fatorial de 0" (fatorial 0) 1) teste2 = TestCase (assertEqual "Teste do fatorial de 1" (fatorial 1) 1) teste3 = TestCase (assertEqual "Teste do fatorial de 2" (fatorial 2) 2) teste4 = TestCase (assertEqual "Teste do fatorial de 3" (fatorial 3) 6) teste5 = TestCase (assertEqual "Teste do fatorial de 4" (fatorial 4) 24) teste6 = TestCase (assertEqual "Teste do fatorial de 5" (fatorial 5) 120) teste7 = TestCase (assertEqual "Teste do fatorial de 7" (fatorial 7) 5040) teste8 = TestCase (assertEqual "Teste do fatorial de 10" (fatorial 10) 3628800) teste9 = TestCase (assertEqual "Teste do fatorial de 15" (fatorial 15) 2004310016) Cada caso de teste desse testa se um fatorial de um dado número é igual ao resultado esperado. Se, por exemplo, na 2ª linha o "fatorial 1" retornar algo diferente de 1, ele indica que o teste falhou e não passou. Vamos construir agora mais alguns casos de testes. teste10 = TestCase (assertEqual "Teste do fatorial de um numero negativo 1" (fatorial (-1)) (-1)) teste11 = TestCase (assertEqual "Teste do fatorial de um numero negativo 2" (fatorial (-25)) (-1)) teste12 = TestCase (assertEqual "Teste do fatorial de um numero negativo 3" (fatorial (-150)) (-1)) Como foi definido na função, se um número negativo for passado, retorne -1. Estou testando se este comportamento vai mesmo funcionar. Agora vamos combinar os testes. testes = TestList [teste1, teste2, teste3, teste4, teste5, teste6, teste7, teste8, teste9, teste10, teste11, teste12] testar :: IO () testar = do runTestTT testes return () Agora compile com o ghci e rode a função testar. A função runTestTT roda esta lista de testes e exibe na tela o resultado. Existe grandes vantagens em se usar testes automatizados, como a possibilidade de rodar os testes novamente para verificar se uma alteração no programa não prejudicou o que já estava funcionando (bem comum!!!). É uma prática bastante usada na Engenharia de Software e o ponto central de Desenvolvimento Orientado a Testes, e é uma boa prática de programação. Este foi um tutorial básico do básico sobre o HUnit e testes automatizados, há um guia mais detalhado sobre o HUnit na página dele: http://hunit.sourceforge.net/HUnit-1.0/Guide.html.
Compartilhar