Haskell udostępnia framework do testów jednostkowych o nazwie HUnit, który (podobno ;) ) inspirowany jest JUnitem z Javy. Pełna dokumentacja: HUnit Documentation
HUnit nie jest domyślnie instalowany wraz z ghc/ghci. Można go doinstalować z repozytorium:
sudo apt-get install libghc-hunit-dev
Podstawowym typem w HUnit 'Assertion' produkująca w wyniku zawsze 'IO ()', aby można było testować fragmenty kodu z tzw. side-effects. Type ten zwracany jest przez szereg funkcji opisanych w tabeli poniżej i to za ich pomocą budować będziemy testy.
`Assertion` stanowią z kolei podstawowy element `Test`, który jest juz gotowym testem jednoskowym (lub ich listą). Testy uruchamiamy funkcją runtestTT, która zwraca typ 'Counts', stanowiący podsumowanie testów.
Typ | Definicja | Opis |
---|---|---|
Assertion | type Assertion = IO () | Podstawowy element każdego testu |
Count | data Counts = Counts { cases, tried, errors, failures :: Int } deriving (Eq, Show, Read) | Raport z wykonania testów |
Test | data Test = TestCase Assertion | TestList [Test] | TestLabel String Test | Test jednostkowy |
Funckja | Sygnatura |
---|---|
assert | Assertable t => t -> Assertion |
assertFailure | assertFailure :: String -> Assertion |
assertBool | String -> Bool -> Assertion |
assertEqual | (Show a, Eq a) => String -> a -> a -> Assertion |
assertionPredicate | AssertionPredicable t => t -> AssertionPredicate |
assertString | String -> Assertion |
runTestTT | Test -> IO Counts |
import Test.HUnit testSum = TestCase $ assertEqual "10 + 5 = 15" 15 (10 + 5) testProd = TestCase $ assertEqual "10 * 15" 150 (10 * 15) testPred = TestCase $ assertBool "10 > 5" (10 > 5) testFailure = TestCase $ assertEqual "It will fail 10 + 2 = 15" (10 + 2) 15 testlist = TestList [TestLabel "testSum" testSum, TestLabel "testPred" testPred, TestLabel "testFailure" testFailure, TestLabel "testProd" testProd ] main :: IO () main = do runTestTT testlist return ()
Funkcje z tabeli z wcześniejszej sekcji można zastąpić specjalnymi operatorami, przedstawionymi poniżej.
Opis | Operator | Sygnatura |
---|---|---|
Assert Bool (True) | (@?) | (AssertionPredicable t) => t -> String -> Assertion |
Assert Equal | (@=?) | (Show a, Eq a) => a: expected -> a: value -> Assertion |
Assert Equal | (@?=) | (Eq a, Show a) => a: value -> a: expected -> Assertion |
Tworzy test o danej etykiecie | (~:) | Testable t => String -> t -> Test |
Tworzy test assertEqual | (~?=) | (Show a, Eq a) => a: value -> a: expected -> Test |
Tworzy test assertEqual | (~=?) | (Show a, Eq a) => a: expected -> a: value -> Test |
import Test.HUnit fact 1 = 1 fact n = n * fact (n - 1) testlist = TestList ["fact 1" ~: fact 1 ~?= 1 , "fact 2" ~: fact 2 ~?= 2 , "fact 3" ~: fact 3 ~?= 6 , "fact 4" ~: fact 4 ~?= 24 , "fact 5" ~: fact 5 ~?= 120 ] main :: IO () main = do runTestTT testlist return ()
Innym ciekawym rozwiązaniem do testowania jest HSpec Do rozwiązania zadań wykorzystaj dowolnie HUnit, albo HSpec (jeśli jest dostępny)
main = do line <- getLine if null line then return () else do putStrLn $ reverseWords line main reverseWords :: String -> String reverseWords = unwords . map reverse . words