Simple haskell unit testing
Asked Answered
H

2

53

I want to go through 99 Haskell Problems, and I want to concentrate on the solution but with testing. If I have the solution to the first problem as a 3 line .hs file,

myLast :: [a] -> a
myLast [x] = x
myLast (_:xs) = myLast xs

What is the minimal amount of code I can add to this so that I can add tests inline and run them with runhaskell?

Highhanded answered 16/4, 2011 at 1:39 Comment(0)
T
66

QuickCheck (which basicaly generates test inputs for you) is probably the best way to test pure function. And if a function in question has an analog from the standard library you can just test your function using the standard one as a model:

{-# LANGUAGE TemplateHaskell #-}

import Test.QuickCheck
import Test.QuickCheck.All

myLast :: [a] -> a
myLast [x] = x
myLast (_:xs) = myLast xs

-- here we specify that 'myLast' should return exactly the same result
-- as 'last' for any given 'xs'
prop_myLast xs = myLast xs == last xs


return [] -- need this for GHC 7.8
-- quickCheckAll generates test cases for all 'prop_*' properties
main = $(quickCheckAll)

If you run it you'll get:

=== prop_myLast on tmp3.hs:12 ===
*** Failed! Exception: 'tmp3.hs:(7,1)-(8,25): Non-exhaustive patterns in function myLast' (after 1 test):  
[]
False

because your myLast doesn't handle [] case (it should but should probably throw an error like 'last'). But here we can simply adjust our test but specifying that only non-empty strings should be used (using ==> combinator):

prop_myLast xs = length xs > 0 ==> myLast xs == last xs

Which makes all 100 auto-generated test cases to pass for myLast:

=== prop_myLast on tmp3.hs:11 ===
+++ OK, passed 100 tests.
True

PS Another way to specify myLast behavior may be:

prop_myLast2 x xs = myLast (xs++[x]) == x

Or better:

prop_myLast3 x xs = x `notElem` xs ==> myLast (xs++[x]) == x
Tinware answered 16/4, 2011 at 2:20 Comment(5)
When I run my file with runhaskell lists.hs it gives me "lists.hs:44:8: parse error on input `$'" Is this how I'm supposed to run it? gist.github.com/923814Highhanded
Try adding {-# LANGUAGE TemplateHaskell #-} as per my example. If your runhaskell point to runghc this should do the trick. Also rename prop_last' to just prop_last - otherwise QuickCheck seems to ignore it.Erudite
So that all works (thanks!), but now how do I make quickcheck catch the errors? I added last' [] = error "empty list"Highhanded
I ended up using the properties/filters for QuickCheck (as you said) since from my searching, I can't tell QuickCheck that it should expect an error, but only that it shouldn't run those.Highhanded
This is a bit out of date. As noted in my answer here, GHC 7.8 requires a line with return [] (or something else to break up TH processing) between the prop_s and the quickCheckAll.Raber
N
2

hspec is also a testing framework for Haskell, which is inspired by Ruby RSpec. It integrates with QuickCheck, SmallCheck, and HUnit.

Nickienicklaus answered 6/10, 2015 at 23:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.