Testy jednostkowe z HUnit
Haskell udostępnia framework do testów jednostkowych o nazwie HUnit, który (podobno ;) ) inspirowany jest JUnitem z Javy.
Pełna dokumentacja: HUnit Documentation
Instalacja
HUnit nie jest domyślnie instalowany wraz z ghc/ghci.
Można go doinstalować z repozytorium:
sudo apt-get install libghc-hunit-dev
Funkcje i typy w HUnit
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
|
Przykłady
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 ()
Operatory w HUnit
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
|
Przykłady
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 ()
HScpec
Innym ciekawym rozwiązaniem do testowania jest HSpec
Do rozwiązania zadań wykorzystaj dowolnie HUnit, albo HSpec (jeśli jest dostępny)
Zadania
-
Dla programu poniżej, który za zadanie ma odwracanie Stringa napisz testy sprawdzające czy funkcja działa poprawnie dla pustego napisu, dla napisu z dużymi literami (Szymon - nomyzS), dla stringów ze spacjami: „Ala ma kota” - „otak am alA” itp,:
main = do
line <- getLine
if null line
then return ()
else do
putStrLn $ reverseWords line
main
reverseWords :: String -> String
reverseWords = unwords . map reverse . words
Napisz testy jednostkowe dla implementacji drzewa binarnego z zajęć
Monady i Typy dla każdej z funkcji.