unit-testing haskell

unit testing - Prueba de unidad Haskell



unit-testing (3)

En general, cualquier proyecto significativo de Haskell se ejecuta con Cabal . Esto se ocupa de la construcción, la distribución, la documentación (con la ayuda de eglefino) y las pruebas.

El enfoque estándar es colocar sus pruebas en el directorio de test y luego configurar un conjunto de pruebas en su archivo .cabal . Esto se detalla en el manual del usuario . Esto es lo que parece el banco de pruebas para uno de mis proyectos

Test-Suite test-melody type: exitcode-stdio-1.0 main-is: Main.hs hs-source-dirs: test build-depends: base >=4.6 && <4.7, test-framework, test-framework-hunit, HUnit, containers == 0.5.*

Luego, en la test/Main.hs archivo test/Main.hs

import Test.HUnit import Test.Framework import Test.Framework.Providers.HUnit import Data.Monoid import Control.Monad import Utils pushTest :: Assertion pushTest = [NumLit 1] ^? push (NumLit 1) pushPopTest :: Assertion pushPopTest = [] ^? (push (NumLit 0) >> void pop) main :: IO () main = defaultMainWithOpts [testCase "push" pushTest ,testCase "push-pop" pushPopTest] mempty

Donde Utils define algunas interfaces más agradables sobre HUnit .

Para pruebas de peso más ligero, le recomiendo que use QuickCheck . Te permite escribir propiedades cortas y probarlas a través de una serie de entradas aleatorias. Por ejemplo:

-- Tests.hs import Test.QuickCheck prop_reverseReverse :: [Int] -> Bool prop_reverseReverse xs = reverse (reverse xs) == xs

Y entonces

$ ghci Tests.hs > import Test.QuickCheck > quickCheck prop_reverseReverse .... Passed Tests (100/100)

Soy nuevo en Haskell y trabajo en pruebas unitarias, sin embargo, encuentro que el ecosistema es muy confuso. Estoy confundido en cuanto a la relación entre HTF y HUnit.

En algunos ejemplos veo que configura casos de prueba, los exporta en una lista de pruebas y luego ejecuta ghci con runTestsTT (como este ejemplo de HUnit ).

En otros ejemplos, creas un corredor de prueba vinculado al archivo cabal que usa algo de magia de preprocesador para encontrar tus pruebas, como en este ejemplo de git . También parece que las pruebas de HTF necesitan un prefijo test_ o no se ejecutan? Me costó encontrar documentación sobre eso, solo noté el patrón que todos tenían.

De todos modos, ¿alguien puede ayudar a resolver esto por mí? ¿Qué se considera la forma estándar de hacer las cosas en Haskell? ¿Cuáles son las mejores prácticas? ¿Cuál es el más fácil de configurar y mantener?


También soy novato haskeller y esta introducción me ha sido de gran ayuda: " Comenzando con HUnit ". Para resumir, voy a poner aquí un ejemplo de prueba simple del uso de HUnit sin el archivo de proyecto .cabal :

Supongamos que tenemos el módulo SafePrelude.hs :

module SafePrelude where safeHead :: [a] -> Maybe a safeHead [] = Nothing safeHead (x:_) = Just x

podemos poner pruebas en TestSafePrelude.hs siguiente manera:

module TestSafePrelude where import Test.HUnit import SafePrelude testSafeHeadForEmptyList :: Test testSafeHeadForEmptyList = TestCase $ assertEqual "Should return Nothing for empty list" Nothing (safeHead ([]::[Int])) testSafeHeadForNonEmptyList :: Test testSafeHeadForNonEmptyList = TestCase $ assertEqual "Should return (Just head) for non empty list" (Just 1) (safeHead ([1]::[Int])) main :: IO Counts main = runTestTT $ TestList [testSafeHeadForEmptyList, testSafeHeadForNonEmptyList]

Ahora es fácil ejecutar pruebas usando ghc :

runghc TestSafePrelude.hs

o hugs : en este caso, TestSafePrelude.hs debe renombrarse como Main.hs (siempre que esté familiarizado con los abrazos) (no olvide cambiar también el encabezado del módulo):

runhugs Main.hs

o cualquier otro compilador haskell ;-)

Por supuesto, hay más que eso en HUnit , así que realmente recomiendo leer el tutorial sugerido y la Guía del usuario de la biblioteca.


Usted ha respondido la mayoría de sus preguntas, pero también ha preguntado sobre HTF y cómo funciona.

HTF es un marco que está diseñado para ambas pruebas unitarias: es compatible con HUnit (lo integra y lo envuelve para proporcionar funciones adicionales) y las pruebas basadas en propiedades, se integra con la comprobación rápida. Utiliza un preprocesador para ubicar las pruebas para que no tenga que crear una lista manualmente. El preprocesador se agrega a los archivos fuente de prueba con un pragma:

{-# OPTIONS_GHC -F -pgmF htfpp #-}

(Alternativamente, supongo que podrías agregar las mismas opciones a tu propiedad ghc-options en tu archivo cabal, pero nunca lo intenté, así que no sé si es útil o no).

El preprocesador escanea su módulo para funciones de alto nivel denominadas test_xxxx o prop_xxxx y las agrega a una lista de pruebas para el módulo. Puede usar esta lista directamente poniendo una función main en el módulo y ejecutándolas ( main = htfMain htf_thisModuleTests ) o main = htfMain htf_thisModuleTests desde el módulo, y tiene un programa de prueba principal para múltiples módulos, que importa los módulos con pruebas y ejecuta todo de ellos:

import {-@ HTF_TESTS @-} ModuleA import {-@ HTF_TESTS @-} ModuleB main :: IO () main = htfMain htf_importedTests

Este programa puede integrarse con cabal usando la técnica descrita por @jozefg, o cargarse en ghci y ejecutarse de forma interactiva (aunque no en Windows - ver https://github.com/skogsbaer/HTF/issues/60 para más detalles).

Tasty es otra alternativa que proporciona una forma de integrar diferentes tipos de pruebas. No tiene un preprocesador como HTF, pero tiene un módulo que realiza funciones similares con Template Haskell . Al igual que HTF, también se basa en la convención de nombres para identificar sus pruebas (en este caso, case_xxxx vez de test_xxxx ). Además de las pruebas HUnit y QuickCheck, también tiene módulos para manejar varios otros tipos de prueba.