正如Alexander Poluektov指出的那样,你试图测试的代码可以很容易地分成纯函数和不纯函数两个部分。尽管如此,我认为了解如何在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 for monadic code针对模型进行属性测试:
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.
当然,根据您的用例,您也可以测试一些其他属性。
*有关我使用术语模拟行为的说明:
从面向对象的角度来看,“模拟”这个术语可能不是最好的。但是“模拟”的目的是什么呢?
它使您能够测试需要访问通常无法在测试时获得或不易控制且难以验证的资源的代码。
- 要么在测试时不可用
- 要么不易于控制,因此不易于验证。
通过将提供此类资源的责任转移给 quickcheck,突然之间可以为测试下的代码提供可在测试运行后验证的环境。
马丁·福勒在一篇有关模拟的文章中描述得非常好:
“模拟是...预先编程的对象,它们带有期望,形成了对它们所期望接收的调用的规范。”
对于 quickcheck 设置而言,生成作为输入的文件已“预先编程”,以便我们了解它们的大小(== 期望)。然后根据我们的规范(== 属性)对它们进行验证。
length
函数吗? - Alexander Poluektovlength
更有趣,那么您可以轻松地测试 该 函数,该函数将是String -> a
的某个值a
。 - Dan BurtonunsafePerformIO
才是不纯的 ;) - alternative