Testing functions in Haskell that do IO

后端 未结 5 2125
心在旅途
心在旅途 2020-12-23 17:06

Working through Real World Haskell right now. Here\'s a solution to a very early exercise in the book:

-- | 4) Counts the number of characters in a file
numC         


        
5条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-12-23 17:40

    You can make your code testable by using a type-class-constrained type variable instead of IO.

    First, let's get the imports out of the way.

    {-# LANGUAGE FlexibleInstances #-}
    import qualified Prelude
    import Prelude hiding(readFile)
    import Control.Monad.State
    

    The code we want to test:

    class Monad m => FSMonad m where
        readFile :: FilePath -> m String
    
    -- | 4) Counts the number of characters in a file
    numCharactersInFile :: FSMonad m => FilePath -> m Int
    numCharactersInFile fileName = do
        contents <- readFile fileName
        return (length contents)
    

    Later, we can run it:

    instance FSMonad IO where
        readFile = Prelude.readFile
    

    And test it too:

    data MockFS = SingleFile FilePath String
    
    instance FSMonad (State MockFS) where 
                   -- ^ Reader would be enough in this particular case though
        readFile pathRequested = do
            (SingleFile pathExisting contents) <- get
            if pathExisting == pathRequested
                then return contents
                else fail "file not found"
    
    
    testNumCharactersInFile :: Bool
    testNumCharactersInFile =
        evalState
            (numCharactersInFile "test.txt") 
            (SingleFile "test.txt" "hello world")
          == 11
    

    This way your code under test needs very little modification.

提交回复
热议问题