Я пытаюсь протестировать небольшую функцию (точнее, IO Action), которая принимает аргумент командной строки и выводит ее на экран. Мой оригинал (непроверяема) функция:Mocking IO Actions: getArgs и putStrLn
-- In Library.hs
module Library where
import System.Environment (getArgs)
run :: IO()
run = do
args <- getArgs
putStrLn $ head args
Посмотрев на this answer about mocking, я придумал способ издеваться getArgs
и putStrLn
с помощью ограниченного типа класса типа. Таким образом, данная функция становится:
-- 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
Это Library.
, Prelude.
и System.Environment.
, чтобы избежать жалоб компилятора Ambigious Occurence
. Мой тестовый файл выглядит следующим образом.
-- 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")
Я использую State
монаду, которая эффективно содержит 2 поля.
- список для аргументов командной строки, где издеваться
getArgs
читает из - Строка, макет
putStrLn
ставит то, что было передано ему.
Приведенный выше код работает и, кажется, проверяет, что я хочу проверить. Тем не менее, мне интересно, есть ли какой-то лучший/более чистый/более идиоматический способ тестирования этого. Во-первых, я использую одно и то же состояние, чтобы оба помещали материал в тест (мои аргументы в поддельной командной строке), а затем извлекали из него все (что было передано putStrLn
.
Есть ли лучший способ что я делаю? Я больше знаком с издевательством в среде Javascript, и мои знания о Haskell довольно просты (я пришел к вышеуказанному решению с честной пробной ошибкой, а не с реальным пониманием)
Я думаю, что ваш код хороший/чистый/идиоматический. Реальные вопросы - это решение вашей проблемы? Является ли ваша «модель» достаточной для точного представления фактической «файловой системы», которую вы моделируете? Если это так, то, вероятно, нет необходимости переубеждать это. Но 'putStrLn' должен, вероятно, добавить свой аргумент строки в старую строку в состоянии, вместо игнорирования старой строки, если вы действительно хотите симулировать' Prelude.putStrLn'. – user2407038
@ user2407038 Добавление к старой строке звучит как хорошая идея. –