简体   繁体   English

我用一个失败的案例运行“阴谋测试”,但它总是通过

[英]I run "cabal test" with a failing case but it always passes

I want to test a function and I want this test to fail.我想测试一个 function,我希望这个测试失败。 After initiating the project using cabal init , I created my findOddInt function in the Main.hs file in the app directory:使用cabal init启动项目后,我在app目录的Main.hs文件中创建了findOddInt function:

module Main where

findOddInt :: [Int] -> Int
findOddInt (x:xs) = undefined

main :: IO ()
main = putStrLn "Hello, Haskell!"

I created a directory named tests and created the FindOddInt.hs file:我创建了一个名为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

my .cabal file is as follows:我的.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

All set, I ran cabal configure --enable-tests && cabal test and got the output:一切就绪,我运行了cabal configure --enable-tests && cabal test并得到了 output:

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.

I already looked the following posts:我已经看过以下帖子:

These posts don't cover my case.这些帖子不包括我的情况。 Any help is appreciated.任何帮助表示赞赏。

You included the source of your executable as part of your test suite ( hs-source-dirs ) and this confuses the compiler.您将可执行文件的源代码包含在测试套件 ( hs-source-dirs ) 中,这会使编译器感到困惑。 When compiling both tests and regular executables, GHC looks for main in a module named Main , and in this case that is app/Main.hs which does nothing, and your test module is compiled but not actually used.在编译测试和常规可执行文件时,GHC 在名为Main的模块中查找main ,在本例中是app/Main.hs ,它什么都不做,并且您的测试模块已编译但未实际使用。

  • Don't put app in the hs-source-dirs of the test suite.不要将app放在测试套件的hs-source-dirs中。 And more generally, don't include a directory in more than one component (library, executable, test or benchmark suite), unless you know what you're doing.更一般地说,不要在多个组件(库、可执行文件、测试或基准套件)中包含一个目录,除非您知道自己在做什么。 If you need to reuse code, you can put it in a library and have executable and test suite depend on it.如果你需要重用代码,你可以把它放在一个库中,让可执行文件和测试套件依赖它。

  • The file that you put under main-is: in the .cabal file should include module Main where or no module line.您放在main-is:.cabal文件中应该包含module Main where或没有 module 行。 The file name can be anything, but to avoid confusing it with a library module, it may be a good idea to use a lowercase name.文件名可以是任何名称,但为了避免将其与库模块混淆,使用小写名称可能是个好主意。


If the module is not going to be imported by another module (Main, for example), then you are free to use any filename for it.如果该模块不会被另一个模块(例如 Main)导入,那么您可以为它自由使用任何文件名。

--- https://downloads.haskell.org/ghc/latest/docs/users_guide/separate_compilation.html --- https://downloads.haskell.org/ghc/latest/docs/users_guide/separate_compilation.html

You're inconsistently trying to declare two different modules as Main - for the purpose of finding the main entry point you want FindOddInt.hs to be considered Main , but for the purpose of importing things you want Main.hs to be considered Main .您不一致地尝试将两个不同的模块声明为Main - 为了找到您希望FindOddInt.hs被视为Mainmain入口点,但为了导入您希望Main.hs被视为Main的东西。

In your cabal file you have:在你的 cabal 文件中你有:

test-suite tests
    type: exitcode-stdio-1.0
    main-is: FindOddInt.hs

This says that the Main module should be in a file called FindOddInt.hs (in one of the hs-source-dirs ).这表示Main模块应该位于名为FindOddInt.hs的文件中(在hs-source-dirs之一中)。 However in FindOddInt.hs itself you have:但是在FindOddInt.hs本身你有:

module FindOddInt ( main ) where
import Main hiding ( main )

So there you're saying that that same file is not the Main module after all, it's module FindOddInt .所以你是说同一个文件毕竟不是Main模块,它是module FindOddInt And to make matters worse you're actually importing from another module called Main (which the compiler is going to have to find by the usual module to filename convention, so it'll look for a Main.hs file in any of the hs-source-dirs ).更糟糕的是,您实际上是从另一个名为Main的模块导入的(编译器必须按照通常的模块文件名约定来查找该模块,因此它会在hs-source-dirs Main.hs hs-source-dirs )。

Apparently the way this ends up being compiled, the compiler ends up actually using the Main.hs file as the definition of the Main module, effectively overriding the main-is: FindOddInt.hs configuration in the cabal file.显然,这最终被编译的方式,编译器最终实际上使用Main.hs文件作为Main模块的定义,有效地覆盖了main-is: FindOddInt.hs配置。 That means your test suite just runs putStrLn "Hello, Haskell!"这意味着你的测试套件只是运行putStrLn "Hello, Haskell!" which of course succeeds.这当然成功了。 You can confirm this by running cabal test --test-show-details=direct , so that it shows the output from successful tests as well as failures.您可以通过运行cabal test --test-show-details=direct来确认这一点,以便它显示来自成功测试和失败测试的 output。 I get something like this:我得到这样的东西:

Build profile: -w ghc-9.0.2 -O1
In order, the following will be built (use -v for more details):
 - find-odd-int-0.1.0.0 (test:tests) (first run)
Preprocessing test suite 'tests' for find-odd-int-0.1.0.0..
Building test suite 'tests' for find-odd-int-0.1.0.0..
Running 1 test suites...
Test suite tests: RUNNING...
Hello, Haskell!
Test suite tests: PASS
Test suite logged to:
/tmp/scratch/dist-newstyle/build/x86_64-linux/ghc-9.0.2/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.

You can see the "Hello, Haskell" being printed there.你可以看到那里打印了“Hello, Haskell”。

If you change your FindOddInt.hs file so that its header is module Main (since your cabal file says that is the Main module), then you get this error:如果您更改您的FindOddInt.hs文件,使其 header 是module Main (因为您的 cabal 文件说这是Main模块),那么您会收到此错误:

<no location info>: error:
    module ‘main:Main’ is defined in multiple files: tests/FindOddInt.hs
                                                     tests/FindOddInt.hs

That's because your other-modules: Main is telling it to look for the Main module again.那是因为你的other-modules: Main告诉它再次寻找Main模块。 If you remove that , you get:如果你删除,你会得到:

Building test suite 'tests' for find-odd-int-0.1.0.0..
Module imports form a cycle:
  module ‘Main’ (tests/FindOddInt.hs) imports itself

This is because the import Main is now being resolved against FindOddInt.hs , not Main.hs .这是因为现在正在根据FindOddInt.hs而非Main.hs解析import Main

Basically, there is no way to make this work the way you seem to want it to.基本上,没有办法按照您希望的方式进行这项工作。 It is arguably a bug in Cabal and/or GHC that your original config compiles at all;可以说这是 Cabal 和/或 GHC 中的一个错误,您的原始配置根本无法编译; it should possibly report an error about the mismatch between your cabal files's main-is and the referenced file's own decalared module name (and if not, it's extremely surprising that it uses another file as Main instead of that one).它可能会报告有关 cabal 文件的main-is和引用文件自己的已声明模块名称之间不匹配的错误(如果不是,它使用另一个文件而不是那个文件作为Main是非常令人惊讶的)。 But no amount of fixing problems upstream is going to solve the fundamental contradiction in what you're trying to do.但是,再多的上游问题修复也无法解决您尝试做的事情中的根本矛盾。 Either FindOddInt.hs is Main (in which case you can't import things from another Main ), or it isn't. FindOddInt.hs要么是Main (在这种情况下您不能从另一个Main导入内容),要么不是。

In my experience, it is not really a good idea to ever import something from Main .根据我的经验,从Main导入一些东西并不是一个好主意。 A source file for Main can only really be included in a single application, so as soon as you want 2 application entry points (like your normal executable and a test suite), any code you want to share between them can't be in Main . Main的源文件只能真正包含在单个应用程序中,因此一旦您想要 2 个应用程序入口点(如普通可执行文件和测试套件),您想要在它们之间共享的任何代码都不能在Main中.

What this ends up meaning for my coding practices is that I only put very minimal code in Main that I don't intend to ever test with a library-level test suite (I might do further end-to-end testing of the whole binary, but that's different).这最终对我的编码实践意味着什么,我只在Main中放置了非常少的代码,我不打算使用库级测试套件进行测试(我可能会对整个二进制文件进行进一步的端到端测试,但那是不同的)。 Most of my code is typically in an internal library, so it can be imported by both my Main module and by my test suite;我的大部分代码通常都在一个内部库中,因此它可以被我的Main模块和我的测试套件导入; my actual app folder will usually only have a very thin Main.hs that stitches together a definition for main that is just combining imported things from the library (sometimes even it's just main = SomeModule.realMain , if I wanted run library tests on realMain ).我实际的app文件夹通常只有一个非常薄的Main.hs ,它将main的定义拼接在一起,该定义只是组合从库中导入的东西(有时甚至只是main = SomeModule.realMain ,如果我想在realMain上运行库测试) .

I also agree with Li-yao Xia's recommendation that you don't put the same folder in the hs-source-dirs of multiple components.我也同意夏丽瑶的建议,不要将同一个文件夹放在多个组件的hs-source-dirs中。 It's possible to do that sensibly (and is one option for giving your test suite access to modules that a library doesn't expose), but it also has drawbacks even when it doesn't cause errors like this one (mainly that you waste time compiling files multiple times for each component, instead of compiling once and then linking them).明智地这样做是可能的(并且是让您的测试套件访问库未公开的模块的一种选择),但它也有缺点,即使它不会导致像这样的错误(主要是你浪费时间为每个组件多次编译文件,而不是编译一次然后链接它们)。 One folder per component is the simplest and easiest way to structure your project, unless you have a good reason otherwise.每个组件一个文件夹是构建项目的最简单和最简单的方法,除非您有充分的理由。

For myself, I don't like giving Main a different name than Main.hs .对于我自己,我不喜欢给Main取一个与Main.hs不同的名字。 You still can't have another Main.hs that is a different module, so I think it avoids confusion all round to stick firmly to the usual module naming convention and have the Main.hs filename be occupied by the Main module.你仍然不能有另一个不同模块的Main.hs ,所以我认为它可以避免混淆,坚定地坚持通常的模块命名约定并让Main.hs文件名被Main模块占用。 You likely would have spotted what was happening if you had done that and tried to have a Main.hs importing from Main (and the compiler certainly would have spotted the two files claiming to be Main , as in an error I quoted above).如果您这样做并尝试从Main导入Main.hs ,您可能会发现发生了什么(并且编译器肯定会发现这两个文件声称是Main ,就像我上面引用的错误一样)。 There's not much additional safety in putting Main in a file with a name that can't be imported;Main放在一个名称无法导入的文件中并没有多少额外的安全性; it just enforces the "don't import from Main " rule that you end up having to stick to anyway.它只是强制执行“不要从Main导入”规则,您最终不得不坚持这一规则。 This makes main-is: Main.hs a pointless bit of boilerplate, but at least you don't have to think about it.这使得main-is: Main.hs成为毫无意义的样板文件,但至少您不必考虑它。

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

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