packages feed

monad-mock-0.2.0.0: test-suite/Control/Monad/MockSpec.hs

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FunctionalDependencies #-}

module Control.Monad.MockSpec (spec) where

import Prelude hiding (readFile, writeFile)

import Control.Exception (evaluate)
import Control.Monad.Except (MonadError, runExcept)
import Data.Function ((&))
import Test.Hspec

import Control.Monad.Mock
import Control.Monad.Mock.TH

class MonadError e m => MonadFileSystem e m | m -> e where
  readFile :: FilePath -> m String
  writeFile :: FilePath -> String -> m ()
makeAction "FileSystemAction" [ts| MonadFileSystem String |]

copyFileAndReturn :: MonadFileSystem e m => FilePath -> FilePath -> m String
copyFileAndReturn a b = do
  x <- readFile a
  writeFile b x
  return x

spec :: Spec
spec = describe "MockT" $ do
  it "runs computations with mocked method implementations" $ do
    let result = copyFileAndReturn "foo.txt" "bar.txt"
          & runMockT [ ReadFile "foo.txt" :-> "file contents"
                     , WriteFile "bar.txt" "file contents" :-> () ]
          & runExcept
    result `shouldBe` Right "file contents"

  it "raises an exception if calls are not in the right order" $ do
    let result = copyFileAndReturn "foo.txt" "bar.txt"
          & runMockT [ WriteFile "bar.txt" "file contents" :-> ()
                     , ReadFile "foo.txt" :-> "file contents" ]
          & runExcept
        exnMessage =
          "runMockT: argument mismatch in readFile\n\
          \  given: ReadFile \"foo.txt\"\n\
          \  expected: WriteFile \"bar.txt\" \"file contents\"\n"
    evaluate result `shouldThrow` errorCall exnMessage

  it "raises an exception if calls are missing" $ do
    let result = copyFileAndReturn "foo.txt" "bar.txt"
          & runMockT [ ReadFile "foo.txt" :-> "file contents"
                     , WriteFile "bar.txt" "file contents" :-> ()
                     , ReadFile "qux.txt" :-> "file contents 2" ]
          & runExcept
        exnMessage =
          "runMockT: expected the following unexecuted actions to be run:\n\
          \  ReadFile \"qux.txt\"\n"
    evaluate result `shouldThrow` errorCall exnMessage

  it "raises an exception if there are too many calls" $ do
    let result = copyFileAndReturn "foo.txt" "bar.txt"
          & runMockT [ ReadFile "foo.txt" :-> "file contents" ]
          & runExcept
        exnMessage =
          "runMockT: expected end of program, called writeFile\n\
          \  given action: WriteFile \"bar.txt\" \"file contents\"\n"
    evaluate result `shouldThrow` errorCall exnMessage