如何使用 Haskell 堆栈项目 运行 多个测试文件

How to Run Multiple Test Files with Haskell Stack Project

我想用 Stack 设置一个现有的 Haskell 项目。现有项目在test目录下使用多个文件;默认情况下,这些单独的测试文件,Stack(或 cabal?)似乎使用单个 test/Spec.hs 进行测试。我怎样才能继续在这个项目中使用多个文件?

注意:我正在学习 Haskell,这个项目是我从 "kata" 学习的方法。所以测试是孤立的,一次只关注语言的一个方面。

下面是这样的目录结构的设置

> tree                                                                                       
.
├── example.cabal
├── app
│   └── Main.hs
├── ChangeLog.md
├── LICENSE
├── Setup.hs
├── src
│   ├── A
│   │   └── C.hs
│   ├── A.hs
│   └── B.hs
├── stack.yaml
└── tst
    ├── integration
    │   └── Spec.hs
    └── unit
        ├── A
        │   └── CSpec.hs
        ├── ASpec.hs
        ├── BSpec.hs
        └── Spec.hs

您希望拥有独立于通常的单元测试的集成测试,以及与您的 src-文件夹

中的每个模块相对应的几个子模块

首先您需要将测试套件添加到您的

example.cabal 文件

name:                example
...
-- copyright:
-- category:
build-type:          Simple
extra-source-files:  ChangeLog.md
cabal-version:       >=1.10

executable testmain
  main-is:       Main.hs
  hs-source-dirs: app
  build-depends: base
               , example

library
  exposed-modules:     A.C,A,B
  -- other-modules:
  -- other-extensions:
  build-depends:       base >=4.9 && <4.10
  hs-source-dirs:      src
  default-language:    Haskell2010

test-suite unit-tests
  type:          exitcode-stdio-1.0
  main-is:       Spec.hs
  hs-source-dirs: tst/unit
  build-depends: base
               , example
               , hspec
               , hspec-discover
               , ...

test-suite integration-tests
  type:          exitcode-stdio-1.0
  main-is:       Spec.hs
  hs-source-dirs: tst/integration
  build-depends: base
               , example
               , hspec
               , ...

将以下内容放入您的 tst/unit/Spec.hs 中,它来自 hspec-discover,它会发现(因此得名)所有形式为 ...Spec.hs 的模块并执行 spec 函数来自每个模块。

tst/unit/Spec.hs

{-# OPTIONS_GHC -F -pgmF hspec-discover #-}

就这一行

其他测试文件

然后在 ASpec.hs 中添加单元测试,在 BSpec.hsCSpec.hs 中添加其他单元测试,在 tst/integration 文件夹中添加 Spec.hs [=28] =]

module ASpec where

import Test.Hspec
import A

spec :: Spec
spec = do
  describe "Prelude.head" $ do
    it "returns the first element of a list" $ do
      head [23 ..] `shouldBe` (23 :: Int)

    it "returns the first element of an *arbitrary* list" $
      property $ \x xs -> head (x:xs) == (x :: Int)

    it "throws an exception if used with an empty list" $ do
      evaluate (head []) `shouldThrow` anyException

然后您可以使用

编译和运行您的测试
$> stack test
# now all your tests are executed
$> stack test :unit-tests
# now only the unit tests run
$> stack test :integration-tests
# now only the integration tests run

来源

您可以在 https://hspec.github.io, if you want to know more about hspec-style testing I guess it would be best to start there. For the stack - go to https://haskellstack.org 找到所有示例 - 那里有一些关于 testing/benchmarking 的信息 - 我的意思是关于 运行 测试和基准测试。

对于 haskell 中的不同测试风格,请参阅 HUnit、QuickCheck、Smallcheck、doctests(如果我忘记了一个,我最亲爱的道歉 - 这些也是我经常使用的)。

这是一个只有堆栈和 HUnit 的解决方案。没有什么反对 hspec、htf、tasty 等,但如果您已经在使用 HUnit,即使没有这些也不需要太多胶水。它不需要编辑 cabal 文件。最初的问题暗示使用 hspec,所以 @epsilonhalbe 仍然更接近该标准。

.
├─stack.yaml
├─package.yaml
├─src/
|   ├─A.hs
|   ├─B.hs
|   ├─Main.hs
├─tst/
|   ├─ATest.hs
|   ├─BTest.hs
|   ├─Main.hs

示例 package.yaml 文件:

name:                example
version:             0.1.0.0

dependencies:
- HUnit >= 1.6.1.0 && < 2

library:
  source-dirs: src

executables:
  example-exe:
    main:                Main.hs
    source-dirs:         src
    dependencies:
    - example

tests:
  example-test:
    main:                Main.hs
    source-dirs:         tst
    dependencies:
    - example
    - HUnit

在 ATest.hs 和 BTest.hs 中以通常的 HUnit 方式声明称为 huTests 的测试列表,例如,

huTests = ["egTest" ~: "a" ~=? "a"].

然后 tst/Main.hs 在一个常见的 HUnit 习语中有胶水(参见例如 ):

import ATest (huTests)
import BTest (huTests)

import System.Exit
import Test.HUnit

main :: IO ()
main = do
    results <- runTestTT $
                    test (ATest.huTests ++ BTest.huTests)
    if errors results + failures results == 0 then
        putStrLn "Tests passed."
    else
        die "Tests failed."