简体   繁体   English

Haskell单元测试

[英]Haskell unit testing

I'm new to haskell and working on unit testing, however I find the ecosystem to be very confusing. 我是haskell的新手,正在进行单元测试,但我发现生态系统非常混乱。 I'm confused as to the relationship between HTF and HUnit. 我对HTF和HUnit之间的关系感到困惑。

In some examples I see you set up test cases, export them in an tests list, and then run in ghci with runTestsTT (like this HUnit example ). 在一些示例中,我看到您设置测试用例,将它们导出到测试列表中,然后使用runTestsTT在ghci中运行( 如此HUnit示例 )。

In other examples, you create a test runner tied into the cabal file that uses some preprocessor magic to find your tests like in this git example . 在其他示例中,您创建一个绑定到cabal文件的测试运行器,该文件使用一些预处理器魔法来查找您的测试,就像在这个git示例中一样 Also it seems that HTF tests need to be prefixed with test_ or they aren't run? 似乎HTF测试需要以test_为前缀,否则它们不会运行? I had a hard time finding any documentation on that, I just noticed the pattern that everyone had. 我很难找到任何相关的文档,我只是注意到每个人都有的模式。

Anyways, can someone help sort this out for me? 无论如何,有人可以帮我解决这个问题吗? What is considered the standard way of doing things in Haskell? 什么被认为是在Haskell做事的标准方式? What are the best practices? 什么是最佳做法? What is the easiest to set up and maintain? 什么是最容易设置和维护?

Generally, any significant Haskell project is run with Cabal . 通常,任何重要的Haskell项目都与Cabal一起运行。 This takes care of building, distribution, documentation (with the help of haddock), and testing. 这需要建立,分发,文档(在haddock的帮助下)和测试。

The standard approach is to put your tests in the test directory and then set up a test suite in your .cabal file. 标准方法是将测试放在test目录中,然后在.cabal文件中设置测试套件。 This is detailed in the user manual . 这在用户手册中有详细说明。 Here's what the test suite for one of my projects looks like 这是我的一个项目的测试套件的样子

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.*

Then in the file test/Main.hs 然后在文件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

Where Utils defines some nicer interfaces over HUnit . Utils定义了一些比HUnit更好的接口。

For lighter-weight testing, I strongly recommend you use QuickCheck . 对于轻量级测试,我强烈建议您使用QuickCheck It lets you write short properties and test them over a series of random inputs. 它允许您编写短属性并通过一系列随机输入对其进行测试。 For example: 例如:

 -- Tests.hs
 import Test.QuickCheck

 prop_reverseReverse :: [Int] -> Bool
 prop_reverseReverse xs = reverse (reverse xs) == xs

And then 然后

 $ ghci Tests.hs
 > import Test.QuickCheck
 > quickCheck prop_reverseReverse
 .... Passed Tests (100/100)

I'm also newbie haskeller and I've found this introduction really helpful: " Getting started with HUnit ". 我也是新手哈斯克勒,我发现这个介绍真有帮助:“ HUnit入门 ”。 To summarize, I'll put here simple testing example of HUnit usage without .cabal project file: 总而言之,我将在这里放置一个没有.cabal项目文件的HUnit用法的简单测试示例:

Let's assume that we have module SafePrelude.hs : 我们假设我们有模块SafePrelude.hs

module SafePrelude where

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

we can put tests into TestSafePrelude.hs as follows: 我们可以将测试放入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]

Now it's easy to run tests using ghc : 现在使用ghc运行测试很容易:

runghc TestSafePrelude.hs

or hugs - in this case TestSafePrelude.hs has to be renamed to Main.hs (as far as I'm familiar with hugs) (don't forget to change module header too): hugs -在这种情况下TestSafePrelude.hs已重命名为Main.hs (据我所熟悉的拥抱)(不要忘记更改模块头太):

runhugs Main.hs

or any other haskell compiler ;-) 或任何其他haskell编译器;-)

Of course there is more then that in HUnit , so I really recommend to read suggested tutorial and library User's Guide . 当然在HUnit有更多,所以我真的建议阅读建议的教程和库用户指南

You've had answers to most of your questions, but you also asked about HTF, and how that works. 你已经得到了大部分问题的答案,但你也问过HTF,以及它是如何工作的。

HTF is a framework that is designed for both unit testing -- it is backwards compatible with HUnit (it integrates and wraps it to provide extra functions) -- and property-based testing -- it integrates with quickcheck. HTF是一个专为单元测试而设计的框架 - 它向后兼容HUnit(它集成并包装它以提供额外的功能) - 以及基于属性的测试 - 它与quickcheck集成。 It uses a preprocessor to locate tests so that you don't have to manually build a list. 它使用预处理器来定位测试,这样您就不必手动构建列表。 The preprocessor is added to your test source files using a pragma: 使用编译指示将预处理器添加到测试源文件中:

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

(alternatively, I guess you could add the same options to your ghc-options property in your cabal file, but I've never tried this so don't know if it is useful or not). (或者,我猜你可以在你的cabal文件中为你的ghc-options属性添加相同的选项,但我从来没有尝试过,所以不知道它是否有用)。

The preprocessor scans your module for top-level functions named test_xxxx or prop_xxxx and adds them to a list of tests for the module. 预处理器扫描模块以test_xxxx名为test_xxxxprop_xxxx顶级函数,并将它们添加到模块的测试列表中。 You can either use this list directly by putting a main function in the module and running them ( main = htfMain htf_thisModuleTests ) or export them from the module, and have a main test program for multiple modules, which imports the modules with tests and runs all of them: 您可以直接使用此列表,方法是在模块中放置一个main函数并运行它们( main = htfMain htf_thisModuleTests )或从模块中导出它们,并为多个模块提供主要测试程序,这些模块通过测试导入模块并运行所有模块他们:

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

This program can be integrated with cabal using the technique described by @jozefg, or loaded into ghci and run interactively (although not on Windows - see https://github.com/skogsbaer/HTF/issues/60 for details). 该程序可以使用@jozefg描述的技术与cabal集成,或者加载到ghci并以交互方式运行(尽管不在Windows上 - 有关详细信息,请参阅https://github.com/skogsbaer/HTF/issues/60 )。

Tasty is another alternative that provides a way of integrating different kinds of tests. Tasty是另一种提供集成不同类型测试的方法。 It doesn't have a preprocessor like HTF, but has a module that performs similar functions using Template Haskell . 它没有像HTF这样的预处理器,但有一个使用Template Haskell执行类似功能的模块。 Like HTF, it also relies on naming convention to identify your tests (in this case, case_xxxx rather than test_xxxx ). 与HTF一样,它也依赖于命名约定来识别您的测试(在这种情况下, case_xxxx而不是test_xxxx )。 In addition to HUnit and QuickCheck tests, it also has modules for handling a number of other test types. 除了HUnit和QuickCheck测试之外,它还具有用于处理许多其他测试类型的模块。

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

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