Property Based Testing with Exceptions - unit-testing

I am writing an introduction to Haskell for my local functional programming group.
As a base I am using the Tasty-testing framework and I want to test the indexing function (!!).
MinimalExample.hs
module MinimalExample where
myIndex :: Int -> [a] -> a
myIndex _ [] = error "index too large"
myIndex 0 (x:_) = x
myIndex n (_:xs) = myIndex (n-1) xs
MinimalTests.hs
module MinimalTests where
import Test.Tasty
import Test.Tasty.SmallCheck as SC
import Test.SmallCheck.Series
import MinimalExample
main :: IO ()
main = defaultMain tests
tests :: TestTree
tests = testGroup "Tests" [scProps]
scProps :: TestTree
scProps = testGroup "(checked by SmallCheck)"
[ SC.testProperty "(!!) == myIndex" $ \lst n ->
lst !! n == myIndex (n::Int) (lst::[Int])
]
The tests should not fail on "too large indices" as the Errors/Exceptions are the same.
The tests should fail with negative input - which could be resolved by adding NonNegative as a constraint on the input, or adding the respective clause in the myIndex-function.
Can I test for exceptions in property based tests?
Or do I have to use (H)Unit-tests like in How do I test for an error in Haskell? or Haskell exceptions and unit testing , in this case how do I choose the Index in the range of 0 to length of the generated testing list.

You could use the spoon package, or write a similar function that would return the exception if you want to test that the exceptions are the same.

Related

How to create a random list in haskell

I'm new to haskell and came across this problem: as the title says, I'm trying to create a list of a given length with random numbers, ranging between some x and y. So for example, if x=0 and y=10, the list would be something like: [0,4,3,8,0,2]. I'd prefer to do it with list comprehension and without any IO's. I've seen some posts about similar topics, but none have been helpful to me so far.
Using System.Random in the random package, this is exactly the function randomRs:
randomRs :: (Random a, RandomGen g) => (a, a) -> g -> [a]
It takes a range of values and a generator and returns a lazy list of random values in that range. You can make a generator from a fixed seed using mkStdGen:
mkStdGen :: Int -> StdGen
For example, here’s a seed that (on my machine, with my particular version of random) coincidentally produces the example values you gave:
> take 6 $ randomRs (0, 10) $ mkStdGen 15915
[0,4,3,8,0,2]
If you want the program to produce different random values on each invocation, you must use IO to seed the random number generator with the current time. For that you can use newStdGen instead:
newStdGen :: IO StdGen
A complete example:
import System.Random (newStdGen, randomRs)
main :: IO ()
main = do
gen <- newStdGen
print $ randomList 6 0 10 gen
randomList
:: (Random a, RandomGen g)
=> Int -> a -> a -> g -> [a]
randomList len lo hi gen = take len $ randomRs (lo, hi) gen
The excellent answer by Jon Purdy solves the problem using library function randomRs. This works fine.
However, there is the drawback that the final state of the random number generator is lost. In the general case, you may have other needs for random numbers, so you need that final state, to use it as the initial state for these other needs.
The loss of the final state is linked to the fact that randomRs provides an unlimited supply of random numbers, so there is no proper final state. But the text of your question mentions “a list of a given length”. So there is no need to use randomRs. Instead, you can use function getRandomR, which returns a single random number, as the basic building block.
The most general way to do this is to use the MonadRandom class with the runRand evaluation function. That gives this sort of source code:
import System.Random
import System.Random.TF -- Threefish algorithm
import Control.Monad.Random
-- monadic action to get *count* random numbers between xmin and xmax
someRandomNumbers :: MonadRandom mr => Int -> Int -> Int -> mr [Int]
someRandomNumbers xmin xmax count =
sequence (replicate count (getRandomR (xmin, xmax)))
main = do
let count = 6
seed = 377708 -- adjusted to get exactly [0,4,3,8,0,2] :-)
randGen0 = mkTFGen seed
action = someRandomNumbers 0 10 count
(xs, randGen1) = runRand action randGen0 -- get final state too
putStrLn $ "random xs = " ++ (show xs)
Program output:
random xs = [0,4,3,8,0,2]
Note 1: instead of the standard generator, I used the Threefish random number generator, because it is known to have better statistical properties.
Note 2: IO is not involved at all, except when finally printing the results.

Haskell Text Parser Combinators to parse a Range Greedily like Regex range notation

In regex you can acquire a range of a parse by doing something like \d{1,5}, which parses a digit 1 to 5 times greedily. Or you do \d{1,5}? to make it lazy.
How would you do this in Haskell's Text.ParserCombinators.ReadP?
My attempt gave this:
rangeParse :: Read a => ReadP a -> [Int] -> ReadP [a]
rangeParse parse ranges = foldr1 (<++) $ fmap (\n -> count n $ parse) ranges
Which if you do it like rangeParse (satisfy isDigit) ([5,4..1]) will perform a greedy parse of digits 1 to 5 times. While if you swap the number sequent to [1..5], you get a lazy parse.
Is there a better or more idiomatic way to do this with parser combinators?
update: the below is wrong - for example
rangeGreedy 2 4 a <* string "aab", the equivalent of regexp a{2,4}aab, doesn't match. The questioner's solution gets this right. I won't delete the answer just yet in case it keeps someone else from making the same mistake.
=========
This isn't a complete answer, just a possible way to write the greedy
version. I haven't found a nice way to do the lazy version.
Define a left-biased version of option that returns Maybes:
greedyOption :: ReadP a -> ReadP (Maybe a)
greedyOption p = (Just <$> p) <++ pure Nothing
Then we can do up to n of something with a replicateM of them:
upToGreedy :: Int -> ReadP a -> ReadP [a]
upToGreedy n p = catMaybes <$> replicateM n (greedyOption p)
To allow a minimum count, do the mandatory part separately and append
it:
rangeGreedy :: Int -> Int -> ReadP a -> ReadP [a]
rangeGreedy lo hi p = (++) <$> count lo p <*> upToGreedy (hi - lo) p
The rest of my test code in case it's useful for anyone:
module Main where
import Control.Monad (replicateM)
import Data.Maybe (catMaybes)
import Text.ParserCombinators.ReadP
main :: IO ()
main = mapM_ go ["aaaaa", "aaaab", "aaabb", "aabbb", "abbbb", "bbbbb"]
where
go = print . map fst . readP_to_S test
test :: ReadP [String]
test = ((++) <$> rangeGreedy 2 4 a <*> many aOrB) <* eof
where
a = char 'a' *> pure "ay"
aOrB = (char 'a' +++ char 'b') *> pure "ayorbee"

Mocking IO Actions: getArgs and putStrLn

I'm trying to test a small function (or rather, IO Action) that takes a command line argument and outputs it to the screen. My original (untestable) function is:
-- In Library.hs
module Library where
import System.Environment (getArgs)
run :: IO ()
run = do
args <- getArgs
putStrLn $ head args
After looking at this answer about mocking, I have come up with a way to mock getArgs and putStrLn by using a type class constrained type. So the above function becomes:
-- In Library.hs
module Library where
class Monad m => SystemMonad m where
getArgs :: m [String]
putStrLn :: String -> m ()
instance SystemMonad IO where
getArgs = System.Environment.getArgs
putStrLn = Prelude.putStrLn
run :: SystemMonad m => m ()
run = do
args <- Library.getArgs
Library.putStrLn $ head args
This Library., Prelude. and System.Environment. are to avoid compiler complaints of Ambigious Occurence. My test file looks like the following.
-- In LibrarySpec.hs
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
import Library
import Test.Hspec
import Control.Monad.State
data MockArgsAndResult = MockArgsAndResult [String] String
deriving(Eq, Show)
instance SystemMonad (State MockArgsAndResult) where
getArgs = do
MockArgsAndResult args _ <- get
return args
putStrLn string = do
MockArgsAndResult args _ <- get
put $ MockArgsAndResult args string
return ()
main :: IO ()
main = hspec $ do
describe "run" $ do
it "passes the first command line argument to putStrLn" $ do
(execState run (MockArgsAndResult ["first", "second"] "")) `shouldBe` (MockArgsAndResult ["first", "second"] "first")
I'm using a State monad that effectively contains 2 fields.
A list for the command line arguments where the mock getArgs reads from
A string that the mock putStrLn puts what was passed to it.
The above code works and seems to test what I want it to test. However, I'm wondering if there is some better / cleaner / more idiomatic way of testing this. For one thing, I'm using the same state to both put stuff into the test (my fake command line arguments), and then get stuff out of it (what was passed to putStrLn.
Is there a better way of doing what I'm doing? I'm more familiar with mocking in a Javascript environment, and my knowledge of Haskell is pretty basic (I arrived at the above solution by a fair bit of trial and error, rather than actual understanding)
The better way is to avoid needing to provide mock versions of getArgs and putStrLn by separating out the heart of the computation into a pure function.
Consider this example:
main = do
args <- getArgs
let n = length $ filter (\w -> length w < 5) args
putStrLn $ "Number of small words: " ++ show n
One could say that the heart of the computation is counting the number of small words which is a pure function of type [String] -> Int. This suggest that we should refactor the program like this:
main = do
args <- getArgs
let n = countSmallWords args
putStrLn $ "Number of small words: " ++ show n
countSmallWords :: [String] -> Int
countSmallWords ws = ...
Now we just test countSmallWords, and this is easy because it is pure function.

Haskell exceptions and unit testing

Based on SO question 13350164 How do I test for an error in Haskell?, I'm trying to write a unit test which asserts that given invalid input, a recursive function raises an exception. The approach I adopted works well for non-recursive functions (or when the first call raises the exception), but as soon as the exception occurs deeper in the call chain, the assertion fails.
I've read the excellent answers to question 6537766 Haskell approaches to error handling but unfortunately the advice is a bit too generic for this point of my learning curve. My guess is that the problem here is connected to lazy evaluation and non-pure testing code, but I'd appreciate an expert explanation.
Should I take a different approach to error handling in situations like this (e.g. Maybe or Either), or is there a reasonable fix for making the test case work correctly while using this style?
Here's the code I've come up with. The first two test cases succeed, but the third one fails with "Received no exception, but was expecting exception: Negative item".
import Control.Exception (ErrorCall(ErrorCall), evaluate)
import Test.HUnit.Base ((~?=), Test(TestCase, TestList))
import Test.HUnit.Text (runTestTT)
import Test.HUnit.Tools (assertRaises)
sumPositiveInts :: [Int] -> Int
sumPositiveInts [] = error "Empty list"
sumPositiveInts (x:[]) = x
sumPositiveInts (x:xs) | x >= 0 = x + sumPositiveInts xs
| otherwise = error "Negative item"
instance Eq ErrorCall where
x == y = (show x) == (show y)
assertError msg ex f =
TestCase $ assertRaises msg (ErrorCall ex) $ evaluate f
tests = TestList [
assertError "Empty" "Empty list" (sumPositiveInts ([]))
, assertError "Negative head" "Negative item" (sumPositiveInts ([-1, -1]))
, assertError "Negative second item" "Negative item" (sumPositiveInts ([1, -1]))
]
main = runTestTT tests
It's actually just an error in sumPositiveInts. Your code does not do negativity checking when the only negative number is the last one in the list—the second branch doesn't include the check.
It's worth noting that the canonical way of writing recursion like yours would break the "emptiness" test out in order to avoid this bug. Generally, decomposing your solution into "sum" plus two guards will help to avoid errors.
I second the suggestion from Haskell approaches to error handling by the way. Control.Exception is much more difficult to reason about and learn and error should only be used to mark code branches which are impossible to achieve—I rather like some suggestions that it ought to have been called impossible.
To make the suggestion tangible, we can rebuild this example using Maybe. First, the unguarded function is built in:
sum :: Num a => [a] -> a
then we need to build the two guards (1) empty lists give Nothing and (2) lists containing negative numbers give Nothing.
emptyIsNothing :: [a] -> Maybe [a]
emptyIsNothing [] = Nothing
emptyIsNothing as = Just as
negativeGivesNothing :: [a] -> Maybe [a]
negativeGivesNothing xs | all (>= 0) xs = Just xs
| otherwise = Nothing
and we can combine them as a monad
sumPositiveInts :: [a] -> Maybe a
sumPositiveInts xs = do xs1 <- emptyIsNothing xs
xs2 <- negativeGivesNothing xs1
return (sum xs2)
And then there are lots of idioms and reductions we can employ to make this code much easier to read and write (once you know the conventions!). Let me stress that nothing after this point is necessary nor terribly easy to understand. Learning it improves your ability to decompose functions and fluently think about FP, but I'm just jumping to the advanced stuff.
For instance, we can use "Monadic (.)" (which is also called Kleisli arrow composition) to write sumPositiveInts
sumPositiveInts :: [a] -> Maybe a
sumPositiveInts = emptyIsNothing >=> negativeGivesNothing >=> (return . sum)
and we can simplify both emptyIsNothing and negativeGivesNothing using a combinator
elseNothing :: (a -> Bool) -> a -> Just a
pred `elseNothing` x | pred x = Just x
| otherwise = Nothing
emptyIsNothing = elseNothing null
negativeGivesNothing = sequence . map (elseNothing (>= 0))
where sequence :: [Maybe a] -> Maybe [a] fails an entire list if any of the contained values are Nothing. We can actually go one step further since sequence . map f is a common idiom
negativeGivesNothing = mapM (elseNothing (>= 0))
So, in the end
sumPositives :: [a] -> Maybe a
sumPositives = elseNothing null
>=> mapM (elseNothing (>= 0))
>=> return . sum

Simple haskell unit testing

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?
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
hspec is also a testing framework for Haskell, which is inspired by Ruby RSpec. It integrates with QuickCheck, SmallCheck, and HUnit.