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_xxxxprop_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的模块。