Search code examples
unit-testinghaskellcabal

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. After initiating the project using cabal init, I created my findOddInt function in the Main.hs file in the app directory:

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:

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-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:

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.


Solution

  • You included the source of your executable as part of your test suite (hs-source-dirs) and this confuses the compiler. 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.

    • Don't put app in the hs-source-dirs of the test suite. 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. 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.

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