我想测试一个函数,并且希望这个测试失败。在使用cabal init
启动项目后,我在app
目录下的Main.hs
文件中创建了findOddInt
函数:
module Main where
findOddInt :: [Int] -> Int
findOddInt (x:xs) = undefined
main :: IO ()
main = putStrLn "Hello, Haskell!"
我创建了一个名为tests
的目录,并创建了FindOddInt.hs
文件:
module FindOddInt ( main ) where
import Main hiding ( main )
import Test.HUnit
import qualified System.Exit as Exit
test1 :: Test
test1 = TestCase (assertEqual "should return an odd integer" 3 (findOddInt [0, 1, 0, 1, 0]))
tests :: Test
tests = TestList [TestLabel "test1" test1]
main :: IO ()
main = do
result <- runTestTT tests
if failures result > 0 then Exit.exitFailure else Exit.exitSuccess
我的.cabal
文件如下:
cabal-version: 2.4
name: find-odd-int
version: 0.1.0.0
-- A short (one-line) description of the package.
-- synopsis:
-- A longer description of the package.
-- description:
-- A URL where users can report bugs.
-- bug-reports:
-- The license under which the package is released.
-- license:
author: André Ferreira
maintainer: andresouzafe@gmail.com
-- A copyright notice.
-- copyright:
-- category:
extra-source-files: CHANGELOG.md
executable find-odd-int
main-is: Main.hs
-- Modules included in this executable, other than Main.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
build-depends: base ^>=4.14.3.0
hs-source-dirs: app
default-language: Haskell2010
test-suite tests
type: exitcode-stdio-1.0
main-is: FindOddIntTest.hs
build-depends: base ^>=4.14, HUnit ^>=1.6
hs-source-dirs: app, tests
other-modules: Main
default-language: Haskell2010
设置好后,我运行cabal configure --enable-tests && cabal test
并得到输出:
Build profile: -w ghc-8.10.7 -O1
In order, the following will be built (use -v for more details):
- find-odd-int-0.1.0.0 (test:tests) (file app/Main.hs changed)
Preprocessing test suite 'tests' for find-odd-int-0.1.0.0..
Building test suite 'tests' for find-odd-int-0.1.0.0..
[1 of 2] Compiling Main ( app/Main.hs, /home/asf/Projects/codewars-exercises/haskell/6-kyu/find-odd-int/dist-newstyle/build/x86_64-linux/ghc-8.10.7/find-odd-int-0.1.0.0/t/tests/build/tests/tests-tmp/Main.o )
[2 of 2] Compiling FindOddInt ( tests/FindOddIntTest.hs, /home/asf/Projects/codewars-exercises/haskell/6-kyu/find-odd-int/dist-newstyle/build/x86_64-linux/ghc-8.10.7/find-odd-int-0.1.0.0/t/tests/build/tests/tests-tmp/FindOddInt.o ) [Main changed]
Linking /home/asf/Projects/codewars-exercises/haskell/6-kyu/find-odd-int/dist-newstyle/build/x86_64-linux/ghc-8.10.7/find-odd-int-0.1.0.0/t/tests/build/tests/tests ...
Running 1 test suites...
Test suite tests: RUNNING...
Test suite tests: PASS
Test suite logged to:
/home/asf/Projects/codewars-exercises/haskell/6-kyu/find-odd-int/dist-newstyle/build/x86_64-linux/ghc-8.10.7/find-odd-int-0.1.0.0/t/tests/test/find-odd-int-0.1.0.0-tests.log
1 of 1 test suites (1 of 1 test cases) passed.
我已经看了下面的帖子:
- Running “cabal test” passes although there are test failures
- Create and run a minimal test suite in Haskell using Hunit only
这些帖子不包括我的情况。任何帮助都是感激的。
2条答案
按热度按时间o4tp2gmn1#
您将可执行文件的源代码作为测试套件(
hs-source-dirs
)的一部分包含在内,这会使编译器感到困惑。当编译测试和常规可执行文件时,GHC会在一个名为Main
的模块中查找main
,在本例中是app/Main.hs
,它什么也不做,您的测试模块被编译,但实际上并没有被使用。app
放在测试套件的hs-source-dirs
中。更一般地说,不要在多个组件(库、可执行、测试或基准套件)中包含一个目录,除非你知道你在做什么。如果你需要重用代码,你可以把它放在一个库中,让可执行和测试套件依赖它。.cabal
文件中main-is:
下的文件应该包含module Main where
或不包含模块行。文件名可以是任何内容,但为了避免与库模块混淆,最好使用小写名称。如果该模块不会被另一个模块(例如Main)导入,那么您可以自由地为它使用任何文件名。
--- https://downloads.haskell.org/ghc/latest/docs/users_guide/separate_compilation.html
ovfsdjhp2#
您试图将两个不同的模块声明为
Main
,这是不一致的--为了找到main
入口点,您希望将FindOddInt.hs
视为Main
,但为了导入内容,您希望将Main.hs
视为Main
。在你的阴谋集团档案里,你有:
这意味着
Main
模块 * 应该 * 在一个名为FindOddInt.hs
的文件中(在其中一个hs-source-dirs
中)。然而,在FindOddInt.hs
本身中,您有:所以你说这个文件不是
Main
模块,它是module FindOddInt
。更糟糕的是,你实际上是从另一个名为Main
的模块导入的(编译器必须按照通常的模块到文件名的约定来找到它,所以它会在任何hs-source-dirs
中寻找Main.hs
文件)。显然,编译器最终使用
Main.hs
文件作为Main
模块的定义,有效地覆盖了cabal文件中的main-is: FindOddInt.hs
配置。这意味着您的测试套件只运行putStrLn "Hello, Haskell!"
,当然会成功。您可以通过运行cabal test --test-show-details=direct
来确认这一点。以便显示成功测试和失败测试的输出。你可以看到“你好, haskell ”被印在那里。
如果你改变你的
FindOddInt.hs
文件,使其头文件为module Main
(因为你的cabal文件说那是Main
模块),那么你会得到这个错误:这是因为你的
other-modules: Main
告诉它再次寻找Main
模块。这是因为
import Main
现在是针对FindOddInt.hs
而不是Main.hs
进行解析的。基本上,没有办法让这个工作的方式,你似乎希望它。它可以说是一个bug在阴谋集团和/或GHC,你原来的配置编译在所有;它可能会报告一个关于cabal文件的
main-is
和引用文件自己的decaled模块名不匹配的错误(如果不是,它使用 * 另一个 * 文件作为Main
而不是那个文件,这是非常令人惊讶的)。FindOddInt.hs
要么是Main
(在这种情况下,您不能从另一个Main
导入内容),要么不是。根据我的经验,从
Main
导入代码并不是一个好主意。Main
的源文件只能包含在一个应用程序中,所以只要你想要两个应用程序入口点(比如你的普通可执行文件和一个测试套件),任何你想在它们之间共享的代码都不能在Main
中。对于我的编码实践来说,这意味着我只在
Main
中放置了非常少的代码,我不打算用库级测试套件测试这些代码(我可能会对整个二进制代码做进一步的端到端测试,但那是不同的)。我的实际app
文件夹通常只有一个非常小的Main.hs
,它将main
的定义缝合在一起,而main
只是组合了从库中导入的内容(有时甚至只是main = SomeModule.realMain
,如果我想在realMain
上运行库测试的话)。我也同意夏立耀的建议,多个组件的
hs-source-dirs
中不要放同一个文件夹,这样做是明智的 * 可能 *(并且是一个选项,用于为您的测试套件提供对库不公开的模块的访问),但它也有缺点,即使它不会导致这样的错误(主要是你浪费时间为每个组件编译文件多次,而不是编译一次然后链接它们)每个组件一个文件夹是最简单也是最容易的构建项目的方法,除非你有很好的理由不这样做。就我自己而言,我不喜欢给
Main
和Main.hs
取不同的名字,因此我认为,严格遵循通常的模块命名约定并让Main.hs
文件名被Main
模块占用可以避免混淆。如果您这样做并尝试从Main
导入Main.hs
,您可能会发现正在发生什么(编译器肯定会发现两个声称为Main
的文件,将Main
放入一个名称不能导入的文件中并没有额外的安全性;它只是强制执行了“不要从Main
导入“的规则,而您最终必须坚持这一规则。这使得main-is: Main.hs
成为一个毫无意义的样板文件,但至少您不必考虑它。