I'm very much still a beginner at Haskell. I learn best by trying things out, but sometimes I feel like I'm missing something fundamental. This is one of those times.
I have some code that calls Control.Exception.assert
but the Assertion exception wasn't being thrown. I tracked this down to asserts being ignored by default (which is unusual since the docs suggest the opposite), so I added this to my package.yaml
:
library:
ghc-options:
- -fno-ignore-asserts
I'm not using -O1
or -O2
or -fignore-asserts
so I don't know why the default behaviour is to ignore assert
, but that's not what this question is really about.
I want to write an Hspec unit test to check this behaviour (and also as a chance to learn a bit about Hspec).
In my Lib
module:
-- Lib.hs
module Lib (checkAssert) where
import Control.Exception
checkAssert :: Bool
checkAssert = assert (1 == 2) False
This module is compiled with -fno-ignore-asserts
.
And in my Spec.hs
(I'm actually using Tasty to host the tests):
import Test.Tasty
import Test.Tasty.Hspec
import Lib
import GHC.IO.Unsafe (unsafePerformIO)
import qualified Control.Exception (assert, AssertionFailed)
import Control.Monad
assertionException :: Selector Control.Exception.AssertionFailed
assertionException = const True
-- Hspec tests
spec_assertRaises :: Spec
spec_assertRaises = do
it "assert" $ (liftM checkAssert) `shouldThrow` assertionException
unitTests :: TestTree
unitTests = testGroup "Lib Unit Tests"
[ -- add Hspec tests to Tasty TestTree
unsafePerformIO (testSpec "spec" spec_assertRaises)
]
main :: IO()
main = defaultMain unitTests
This test module is not compiled with -fno-ignore-asserts
. I think this is OK because it doesn't actually call assert
itself, it just calls the function checkAssert
in the Lib
module, which is compiled with that flag.
The problem I'm having is actually related to how non-monadic functions are called from Monads like Spec
. From my reading, I can use fmap
or liftM
to call a non-monadic function like checkAssert
- but I'm not able to get this to compile:
Couldn't match expected type ‘a10 -> r0’ with actual type ‘Bool’
• In the first argument of ‘liftM’, namely ‘checkAssert’
Can someone explain where I'm going wrong with this, please?
Note: I became aware that Test.HUnit.Tools
has assertRaises
and I initially tried to use this instead, but Test.Tasty.HUnit
does not include this function for some reason.