mockcat-1.3.3.0: test/Test/MockCat/WithMockIOSpec.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
module Test.MockCat.WithMockIOSpec (spec) where
import Test.Hspec
import Test.MockCat
import Control.Exception (try, ErrorCall(..), SomeException)
import Control.Concurrent (forkIO)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
import Control.Monad (void)
spec :: Spec
spec = do
describe "withMockIO" $ do
it "can run IO actions directly without liftIO" $ do
withMockIO $ do
f <- mock $ "hello" ~> "world"
f "hello" `shouldBe` "world"
f `shouldBeCalled` "hello"
it "cleans up context even if an exception occurs" $ do
-- Raise an exception in the first test
void $ try @ErrorCall $ withMockIO $ do
void $ mock $ "a" ~> "b"
error "force fail"
-- Verify that no remnants (like expectations) from the previous test remain in the second test
withMockIO $ do
f <- mock $ "x" ~> "y"
f "x" `shouldBe` "y"
-- Verify that the expectation "a" ~> "b" from the previous test is not verified here
it "supports nested withMockIO" $ do
withMockIO $ do
f1 <- mock $ "outer" ~> "ok"
withMockIO $ do
f2 <- mock $ "inner" ~> "ok"
f2 "inner" `shouldBe` "ok"
f2 `shouldBeCalled` "inner"
f1 "outer" `shouldBe` "ok"
f1 `shouldBeCalled` "outer"
it "isolates context between parent and child threads" $ do
mvar <- newEmptyMVar
withMockIO $ do
f <- mock $ "parent" ~> "ok"
void $ forkIO $ do
res <- try @SomeException (mock ("child" ~> "ok") `expects` called once)
putMVar mvar res
f "parent" `shouldBe` "ok"
f `shouldBeCalled` "parent"
childRes <- takeMVar mvar
case childRes of
Left e -> show e `shouldContain` "No WithMockContext found"
Right _ -> fail "Child thread should not have accessed parent context"