繁体   English   中英

Haskell单元测试

[英]Haskell unit testing

我是haskell的新手,正在进行单元测试,但我发现生态系统非常混乱。 我对HTF和HUnit之间的关系感到困惑。

在一些示例中,我看到您设置测试用例,将它们导出到测试列表中,然后使用runTestsTT在ghci中运行( 如此HUnit示例 )。

在其他示例中,您创建一个绑定到cabal文件的测试运行器,该文件使用一些预处理器魔法来查找您的测试,就像在这个git示例中一样 似乎HTF测试需要以test_为前缀,否则它们不会运行? 我很难找到任何相关的文档,我只是注意到每个人都有的模式。

无论如何,有人可以帮我解决这个问题吗? 什么被认为是在Haskell做事的标准方式? 什么是最佳做法? 什么是最容易设置和维护?

通常,任何重要的Haskell项目都与Cabal一起运行。 这需要建立,分发,文档(在haddock的帮助下)和测试。

标准方法是将测试放在test目录中,然后在.cabal文件中设置测试套件。 这在用户手册中有详细说明。 这是我的一个项目的测试套件的样子

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更好的接口。

对于轻量级测试,我强烈建议您使用QuickCheck 它允许您编写短属性并通过一系列随机输入对其进行测试。 例如:

 -- 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)

我也是新手哈斯克勒,我发现这个介绍真有帮助:“ HUnit入门 ”。 总而言之,我将在这里放置一个没有.cabal项目文件的HUnit用法的简单测试示例:

我们假设我们有模块SafePrelude.hs

module SafePrelude where

safeHead :: [a] -> Maybe a
safeHead []    = Nothing
safeHead (x:_) = Just x

我们可以将测试放入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运行测试很容易:

runghc TestSafePrelude.hs

hugs -在这种情况下TestSafePrelude.hs已重命名为Main.hs (据我所熟悉的拥抱)(不要忘记更改模块头太):

runhugs Main.hs

或任何其他haskell编译器;-)

当然在HUnit有更多,所以我真的建议阅读建议的教程和库用户指南

你已经得到了大部分问题的答案,但你也问过HTF,以及它是如何工作的。

HTF是一个专为单元测试而设计的框架 - 它向后兼容HUnit(它集成并包装它以提供额外的功能) - 以及基于属性的测试 - 它与quickcheck集成。 它使用预处理器来定位测试,这样您就不必手动构建列表。 使用编译指示将预处理器添加到测试源文件中:

{-# OPTIONS_GHC -F -pgmF htfpp #-}

(或者,我猜你可以在你的cabal文件中为你的ghc-options属性添加相同的选项,但我从来没有尝试过,所以不知道它是否有用)。

预处理器扫描模块以test_xxxx名为test_xxxxprop_xxxx顶级函数,并将它们添加到模块的测试列表中。 您可以直接使用此列表,方法是在模块中放置一个main函数并运行它们( main = htfMain htf_thisModuleTests )或从模块中导出它们,并为多个模块提供主要测试程序,这些模块通过测试导入模块并运行所有模块他们:

import {-@ HTF_TESTS @-} ModuleA
import {-@ HTF_TESTS @-} ModuleB
main :: IO ()
main = htfMain htf_importedTests

该程序可以使用@jozefg描述的技术与cabal集成,或者加载到ghci并以交互方式运行(尽管不在Windows上 - 有关详细信息,请参阅https://github.com/skogsbaer/HTF/issues/60 )。

Tasty是另一种提供集成不同类型测试的方法。 它没有像HTF这样的预处理器,但有一个使用Template Haskell执行类似功能的模块。 与HTF一样,它也依赖于命名约定来识别您的测试(在这种情况下, case_xxxx而不是test_xxxx )。 除了HUnit和QuickCheck测试之外,它还具有用于处理许多其他测试类型的模块。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM