Ich bin neu in Haskell und arbeite an Unit-Tests, finde das Ökosystem jedoch sehr verwirrend. Ich bin verwirrt über die Beziehung zwischen HTF und HUnit.
In einigen Beispielen sehen Sie, wie Sie Testfälle einrichten, in eine Testliste exportieren und dann in ghci mit ausführen runTestsTT
(wie in diesem HUnit-Beispiel ).
In anderen Beispielen erstellen Sie einen Testläufer, der in die Kabalendatei eingebunden ist und mithilfe von Präprozessor-Magie Ihre Tests wie in diesem Git-Beispiel findet . Es scheint auch, dass HTF-Tests ein Präfix vorangestellt werden müssen test_
oder nicht ausgeführt werden? Es fiel mir schwer, Unterlagen dazu zu finden. Ich bemerkte nur das Muster, das jeder hatte.
Kann mir jemand helfen, das für mich zu klären? Was ist die Standardmethode in Haskell? Was sind die Best Practices? Was ist am einfachsten einzurichten und zu warten?
quelle
tasty
übertest-framework
(HTF?), aber ich sehe auch , dass HTF bekam ein kleines Update letzte Woche, nach mehreren Monaten quiey.Antworten:
Im Allgemeinen wird jedes bedeutende Haskell-Projekt mit Cabal durchgeführt . Dies kümmert sich um das Erstellen, Verteilen, Dokumentieren (mit Hilfe von Schellfisch) und Testen.
Der Standardansatz besteht darin, Ihre Tests in das
test
Verzeichnis zu stellen und dann eine Testsuite in Ihrer.cabal
Datei einzurichten . Dies ist in der Bedienungsanleitung beschrieben . So sieht die Testsuite für eines meiner Projekte ausTest-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.*
Dann in der Datei
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
Wo
Utils
definiert einige schönere Schnittstellen über HUnit .Für Tests mit geringerem Gewicht empfehle ich dringend die Verwendung von QuickCheck . Sie können kurze Eigenschaften schreiben und diese über eine Reihe von zufälligen Eingaben testen. Zum Beispiel:
-- Tests.hs import Test.QuickCheck prop_reverseReverse :: [Int] -> Bool prop_reverseReverse xs = reverse (reverse xs) == xs
Und dann
$ ghci Tests.hs > import Test.QuickCheck > quickCheck prop_reverseReverse .... Passed Tests (100/100)
quelle
Ich bin auch ein Neuling in Sachen Haskeller und fand diese Einführung sehr hilfreich: " Erste Schritte mit HUnit ". Zusammenfassend werde ich hier ein einfaches Testbeispiel für die Verwendung von HUnit ohne
.cabal
Projektdatei vorstellen :Nehmen wir an, wir haben ein Modul
SafePrelude.hs
:module SafePrelude where safeHead :: [a] -> Maybe a safeHead [] = Nothing safeHead (x:_) = Just x
Wir können Tests
TestSafePrelude.hs
wie folgt durchführen: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]
Jetzt ist es einfach, Tests durchzuführen mit
ghc
:runghc TestSafePrelude.hs
oder
hugs
- muss in diesem FallTestSafePrelude.hs
umbenannt werden inMain.hs
(soweit ich mit Umarmungen vertraut bin) (vergessen Sie nicht, auch den Modul-Header zu ändern):runhugs Main.hs
oder irgendein anderer
haskell
Compiler ;-)Natürlich gibt es noch mehr als das
HUnit
, daher empfehle ich wirklich, das empfohlene Tutorial und das Benutzerhandbuch für die Bibliothek zu lesen .quelle
Sie haben Antworten auf die meisten Ihrer Fragen erhalten, aber Sie haben auch nach HTF gefragt und wie das funktioniert.
HTF ist ein Framework, das sowohl für Unit-Tests entwickelt wurde - es ist abwärtskompatibel mit HUnit (es integriert und umschließt es, um zusätzliche Funktionen bereitzustellen) - als auch für eigenschaftsbasierte Tests - es lässt sich in Quickcheck integrieren. Es verwendet einen Präprozessor, um Tests zu lokalisieren, sodass Sie keine Liste manuell erstellen müssen. Der Präprozessor wird Ihren Testquelldateien mithilfe eines Pragmas hinzugefügt:
{-# OPTIONS_GHC -F -pgmF htfpp #-}
(Alternativ könnten Sie Ihrem
ghc-options
Eigentum in Ihrer Kabalendatei die gleichen Optionen hinzufügen , aber ich habe dies noch nie versucht, weiß also nicht, ob es nützlich ist oder nicht).Der Präprozessor durchsucht Ihr Modul nach Funktionen der obersten Ebene mit dem Namen
test_xxxx
oderprop_xxxx
und fügt sie einer Liste von Tests für das Modul hinzu. Sie können diese Liste entweder direkt verwenden, indem Sie einemain
Funktion in das Modul einfügen und sie ausführen (main = htfMain htf_thisModuleTests
) oder sie aus dem Modul exportieren und ein Haupttestprogramm für mehrere Module erstellen, das die Module mit Tests importiert und alle ausführt:import {-@ HTF_TESTS @-} ModuleA import {-@ HTF_TESTS @-} ModuleB main :: IO () main = htfMain htf_importedTests
Dieses Programm kann mit der von @jozefg beschriebenen Technik in cabal integriert oder in ghci geladen und interaktiv ausgeführt werden (obwohl nicht unter Windows - Einzelheiten finden Sie unter https://github.com/skogsbaer/HTF/issues/60 ).
Tasty ist eine weitere Alternative, mit der verschiedene Arten von Tests integriert werden können. Es hat keinen Präprozessor wie HTF, aber ein Modul, das ähnliche Funktionen mit Template Haskell ausführt . Wie bei HTF wird auch hier die Namenskonvention verwendet, um Ihre Tests zu identifizieren (in diesem Fall
case_xxxx
eher alstest_xxxx
). Neben HUnit- und QuickCheck-Tests enthält es auch Module zur Behandlung einer Reihe anderer Testtypen.quelle