Как уже говорил Александр Полуэктов, код, который вы пытаетесь протестировать, легко можно разделить на чистую и нечистую части.
Тем не менее я думаю, что полезно знать, как тестировать такие нечистые функции в haskell.
Обычный подход к тестированию в haskell заключается в использовании quickcheck , и это то, что я также склонен использовать для нечистого кода.
Вот пример того, как вы можете достичь того, что вы пытаетесь сделать, что дает вам насмешливое поведение *:
import Test.QuickCheck
import Test.QuickCheck.Monadic(monadicIO,run,assert)
import System.Directory(removeFile,getTemporaryDirectory)
import System.IO
import Control.Exception(finally,bracket)
numCharactersInFile :: FilePath -> IO Int
numCharactersInFile fileName = do
contents <- readFile fileName
return (length contents)
Теперь предоставьте альтернативную функцию ( Тестирование на модели) :
numAlternative :: FilePath -> IO Integer
numAlternative p = bracket (openFile p ReadMode) hClose hFileSize
Предоставить произвольный экземпляр для тестовой среды:
data TestFile = TestFile String deriving (Eq,Ord,Show)
instance Arbitrary TestFile where
arbitrary = do
n <- choose (0,2000)
testString <- vectorOf n $ elements ['a'..'z']
return $ TestFile testString
Тестирование свойства по модели (с использованием quickcheck для монадического кода ):
prop_charsInFile (TestFile string) =
length string > 0 ==> monadicIO $ do
(res,alternative) <- run $ createTmpFile string $
\p h -> do
alternative <- numAlternative p
testRes <- numCharactersInFile p
return (testRes,alternative)
assert $ res == fromInteger alternative
И небольшая вспомогательная функция:
createTmpFile :: String -> (FilePath -> Handle -> IO a) -> IO a
createTmpFile content func = do
tempdir <- catch getTemporaryDirectory (\_ -> return ".")
(tempfile, temph) <- openTempFile tempdir ""
hPutStr temph content
hFlush temph
hClose temph
finally (func tempfile temph)
(removeFile tempfile)
Это позволит quickCheck создать несколько случайных файлов для вас и проверить вашу реализацию с помощью функции модели.
$ quickCheck prop_charsInFile
+++ OK, passed 100 tests.
Конечно, вы также можете протестировать некоторые другие свойства в зависимости от вашего варианта использования.
* Примечание о моем использовании термина фиктивное поведение :
Термин mock в объектно-ориентированном смысле, возможно, здесь не самый лучший. Но какова цель насмешки?
Это позволяет вам тестировать код, который нуждается в доступе к ресурсу, который обычно
- либо недоступен во время тестирования
- или не легко контролируется и, следовательно, не легко проверить.
Снимая ответственность за предоставление такого ресурса на быструю проверку, неожиданно становится возможным создать среду для тестируемого кода, которую можно проверить после пробного запуска.
Мартин Фаулер хорошо описывает это в статье о насмешках :
«Моты - это ... объекты, предварительно запрограммированные с ожиданиями, которые формируют спецификацию вызовов, которые они ожидают получить».
Для настройки быстрой проверки я бы сказал, что файлы, сгенерированные в качестве входных данных, «запрограммированы» так, что мы знаем об их размере (== ожидание). И затем они проверяются по нашей спецификации (== свойство).