Testing with HUnit
| A test suite | |
| Test cases | |
| Run the tests | |
| Why pure code tests well |
A test suite
A test suite is a second build target in the same .cabal file. It depends on the library and on the test framework, and it has its own main:
test-suite todo-test
type: exitcode-stdio-1.0
main-is: Spec.hs
hs-source-dirs: test
build-depends: base, todo, HUnit >=1.6
default-language: Haskell2010
Keeping the logic in the library is what makes this stanza short. main in the test file only builds and runs the assertions.
Test cases
HUnit offers three ingredients: a label, an assertion with ~?=, and a list of cases:
import Test.HUnit
import Todo (addItem, countItems)
tests = TestList
[ "empty list has no items" ~:? TestCase (countItems [] ~?= 0)
, "one item" ~:? TestCase (countItems (addItem "milk" []) ~?= 1)
]
~?= compares two values and reports both sides when they differ. ~:? attaches a label, which is the line you will read at two in the morning.
Run the tests
The test main prints a summary and exits non-zero on failure, which is what a continuous integration job needs:
main :: IO ()
main = do
counts <- runTestTT tests
when (errors counts + failures counts > 0) exitFailure
cabal test
Cases: 2 Tried: 2 Errors: 0 Failures: 0
cabal test --test-show-details=direct prints the output as it happens instead of hiding it until the end, which matters for a failing case.
Why pure code tests well
Every function in the library is a value transformer, so a test is an expression and needs no setup, no mock, and no teardown:
-- the whole test is one comparison
TestCase (shout "hi" ~?= "HI")
The IO shell from lesson 33 stays thin precisely so that most of the
program lands here. Property based testing in the next lesson takes
the same advantage one step further.
Next:
Property testing with QuickCheck
Article author: Arthur Isaev