组织 Haskell 测试

26

所以,我试图遵循Haskell项目的建议结构,但在组织我的测试时遇到了一些问题。

为了简单起见,让我们从这里开始:

src/Clue/Cards.hs # defines Clue.Cards module
testsuite/tests/Clue/Cards.hs # tests Clue.Cards module

首先,我不确定在testsuite/tests/Clue/Cards.hs中包含测试代码的模块应该命名为什么,其次,我也不知道如何编译我的测试代码以便可以链接到我的源码:

% ghc -c testsuite/tests/Clue/Cards.hs -L src
testsuite/tests/Clue/Cards.hs:5:0:
    Failed to load interface for `Clue.Cards':
      Use -v to see a list of the files searched for.
4个回答

28

我使用Snap Framework为他们的测试套件所采用的方法,基本上可以归纳为:

  1. Use a test-framework such as haskell-test-framework or HTF
  2. Name the modules containing tests by appending .Tests to the module-name containing the IUT, e.g.:

    module Clue.Cards where ... -- module containing IUT
    
    module Clue.Cards.Tests where ... -- module containing tests for IUT
    
  3. By using separate namespaces, you can put your tests in a separate source-folder tests/, you can then use a separate Cabal build-target (see also cabal test-build-target support in recent Cabal versions) for the test-suite which includes the additional source folder in its hs-source-dirs setting, e.g.:

    Executable clue
      hs-source-dirs: src
      ...
    
    Executable clue-testsuite
      hs-source-dirs: src tests
      ...
    

    This works, since there's no namespace collision between the modules in your IUT and the test-suite anymore.


1
+1 提到了 snap-framework,它在这方面非常有条理。 - John L
很好。我正在使用这个项目来学习Haskell生态系统(我不认为有人渴望实现Clue/Cluedo的规则),而且我还没有解决cabal,所以这是一个很好的推动力。我会弄清楚如何使用cabal,然后再回过头来测试。 - rampion
1
关于 snap-framework 项目,可能值得注意的是:他们将测试套件与 hudson 集成,发布测试结果和覆盖率报告(请参见 http://buildbot.snapframework.com/)。 - hvr
将模块命名为Tests.Clue.Cards而不是Clue.Cards.Tests有什么缺点吗? - Saurabh Nanda
抱歉问一个新手问题,如果你将你的模块命名为 Clue.Cards.Tests,那么你的测试文件是不是应该叫做 tests/Clue.Cards.Tests.hs?还是应该叫做 tests/Clue/Cards/Tests.hs - davidA
1
@davidA - 我承认我对Haskell测试还很陌生,但为了让我的测试被找到/编译,我不得不遵循tests/Clue/Cards/Tests.hs文件结构。 - Heuristocrat

3

个人认为对于小型的Haskell项目来说,额外的./src/目录并没有太多意义。当然有源代码,我下载了源代码。

无论是有还是没有src,我建议您进行重构,并拥有一个Clue目录和一个Test目录:

./Clue/Cards.hs   -- module Clue.Cards where ...
./Test/Cards.hs   -- module Test.Cards where ...

这样做可以让 GHCi + Test.Cards 在不需要任何额外参数或使用 cabal 的情况下看到 Clue.Cards。另外,如果您没有使用 cabal + 标志来选择性地构建测试模块,则应该研究一下。

另一个选项是我在许多项目中使用的方式:

./Some/Module/Hierarchy/File.hs
./tests/someTests.hs

我会执行cabal install来安装包,然后运行tests/someTests.hs。如果我的包特别大并且需要很长时间才能安装,那么这可能会很麻烦。


7
我认为添加一个src目录总是有意义的,因为它可以限定源代码层次结构中实际包含的内容:Haskell源代码!当应用程序中还有其他东西存在,例如脚本、配置文件和可能的非Haskell源代码时,这尤其有用。 - fatuhoku

3

这里有另一种方法:

每个模块的单元测试都定义为一个 在模块末尾定义的HUnit TestList,采用一些一致的命名规则,例如“tests_Path_To_Module”。我认为这有助于编写测试,因为我不必在源树中搜索另一个远离的模块,也不必保持两个平行文件层次结构同步。

一个模块的测试列表还包括任何子模块的测试。Hunit的runTestTT运行器内置于应用程序中,并通过test命令访问。这意味着用户可以随时运行测试而无需特殊设置。或者,如果您不喜欢在生产应用中发布测试,请使用CPP和cabal标志仅在dev构建中或在单独的测试运行器可执行文件中包含它们。

还有功能测试,每个文件在tests/目录中有一个或多个,使用shelltestrunner运行,并且一些基于Makefile的开发流程相关测试。


3
不推荐将测试放在你的实现模块中。你应该能够在没有测试框架依赖的情况下编译生产模块。《xUnit设计模式》称此为“生产环境中的测试依赖”。使用 CPP 标志来分离它们不是一个真正的解决方案。无论如何,这都会违反单一职责原则。 - Benjamin Hodgson

1

为了完整起见,值得提到的是通过ghci -i进行小型项目的非常简单的方法。例如,在您的情况下,

>ghci -isrc:testsuite
ghci>:l Clue.Cards
ghci>:l tests.Clue.Cards

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接