Haskellunit testing
我是新来的haskell和unit testing工作,但我觉得生态系统是非常混乱。 我对HTF和HUnit之间的关系感到困惑。
在一些例子中,我看到你设置了testing用例,将它们导出到testing列表中,然后用runTestsTT
在ghci中运行(就像这个HUnit例子 )。
在其他的例子中,你创build一个绑定到cabal文件中的testing运行器,使用一些预处理器魔法来find你的testing,就像这个git的例子 。 也似乎HTFtesting需要前缀test_
或他们不运行? 我很难find任何文件,我只注意到每个人的模式。
无论如何,有人可以帮我解决这个问题吗? 什么被认为是在Haskell中做事的标准方式? 什么是最佳实践? 什么是最容易build立和维护?
通常任何重要的Haskell项目都是在cabal下运行的。 这需要build立,分发,文件(在haddock的帮助下)和testing。
标准的方法是把你的testing放在test
目录中,然后在.cabal
文件中build立一个testing套件。 这在用户手册中有详细介绍。 这是我的一个项目的testing套件的样子
Test-Suite test-melody type: exitcode-stdio-1.0 main-is: Main.hs hs-source-dirs: test build-depends: base >=4.6 && <4.7, test-framework, test-framework-hunit, HUnit, containers == 0.5.*
然后在文件test/Main.hs
import Test.HUnit import Test.Framework import Test.Framework.Providers.HUnit import Data.Monoid import Control.Monad import Utils pushTest :: Assertion pushTest = [NumLit 1] ^? push (NumLit 1) pushPopTest :: Assertion pushPopTest = [] ^? (push (NumLit 0) >> void pop) main :: IO () main = defaultMainWithOpts [testCase "push" pushTest ,testCase "push-pop" pushPopTest] mempty
Utils
在HUnit上定义了一些更好的接口。
对于重量较轻的testing,我强烈build议您使用QuickCheck 。 让我们来编写简短的属性,并通过一系列随机input来testing它们。 例如:
-- Tests.hs import Test.QuickCheck prop_reverseReverse :: [Int] -> Bool prop_reverseReverse xs = reverse (reverse xs) == xs
接着
$ ghci Tests.hs > import Test.QuickCheck > quickCheck prop_reverseReverse .... Passed Tests (100/100)
我也是新手haskeller,我发现这个介绍真的很有用:“ 开始使用HUnit ”。 总结一下,我将把这个简单的HUnittesting例子放在没有.cabal
项目文件的地方:
假设我们有模块SafePrelude.hs
:
module SafePrelude where safeHead :: [a] -> Maybe a safeHead [] = Nothing safeHead (x:_) = Just x
我们可以将testing放入TestSafePrelude.hs
,如下所示:
module TestSafePrelude where import Test.HUnit import SafePrelude testSafeHeadForEmptyList :: Test testSafeHeadForEmptyList = TestCase $ assertEqual "Should return Nothing for empty list" Nothing (safeHead ([]::[Int])) testSafeHeadForNonEmptyList :: Test testSafeHeadForNonEmptyList = TestCase $ assertEqual "Should return (Just head) for non empty list" (Just 1) (safeHead ([1]::[Int])) main :: IO Counts main = runTestTT $ TestList [testSafeHeadForEmptyList, testSafeHeadForNonEmptyList]
现在使用ghc
运行testing很容易:
runghc TestSafePrelude.hs
或hugs
– 在这种情况下, TestSafePrelude.hs
必须重命名为Main.hs
(据我所熟悉的拥抱)(不要忘记更改模块头):
runhugs Main.hs
或任何其他haskell
编译器;-)
当然,在HUnit
还有更多,所以我真的推荐阅读build议的教程和库用户指南 。
你已经回答了你的大部分问题,但你也问过关于HTF,以及如何工作。
HTF是为unit testing而devise的一个框架 – 它与HUnit向后兼容(它集成并包装它以提供额外的function)和基于属性的testing – 它与快速检查集成在一起。 它使用预处理器来定位testing,以便您不必手动构build列表。 预处理器使用附注添加到您的testing源文件中:
{-# OPTIONS_GHC -F -pgmF htfpp #-}
(或者,我想你可以添加相同的选项到你的cabal文件中的ghc-options
属性,但我从来没有试过这个,所以不知道它是否有用)。
预处理器扫描您的模块,查找名为test_xxxx
或prop_xxxx
顶级函数,并将其添加到模块的testing列表中。 你可以直接使用这个列表,通过在模块中放置一个main
函数并运行它们( main = htfMain htf_thisModuleTests
)或者从模块中导出它们,并且有一个用于多个模块的主testing程序,这些模块导入带有testing的模块并运行所有其中:
import {-@ HTF_TESTS @-} ModuleA import {-@ HTF_TESTS @-} ModuleB main :: IO () main = htfMain htf_importedTests
这个程序可以使用@jozefg描述的技术与cabal集成,或者加载到ghci中并交互运行(尽pipe不是在Windows上 – 详见https://github.com/skogsbaer/HTF/issues/60 )。
美味是另一种提供整合不同typestesting的方法。 它没有像HTF这样的预处理器,但是有一个使用Template Haskell执行类似function的模块。 像HTF一样,它也依靠命名约定来识别你的testing(在这种情况下, case_xxxx
而不是test_xxxx
)。 除了HUnit和QuickChecktesting外,它还具有用于处理多种其他testingtypes的模块。