====== 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: [[http://hackage.haskell.org/package/HUnit|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 [[http://hspec.github.io/|HSpec]]
Do rozwiązania zadań wykorzystaj dowolnie HUnit, albo HSpec (jeśli jest dostępny)
===== Zadania =====
- Przerób przykład z sekcji [[pl:dydaktyka:pp:haskell:lab-hunit#przyklady|Testy jednostkowe z HUnit/Przykłady]] z wykorzystaniem operatorów
- 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ęć [[pl:dydaktyka:pp:haskell:lab-monads-types|Monady i Typy]] dla każdej z funkcji.