I have a cabal project that builds an executable.
When I run cabal test --enable-coverage
I get the following error:
All 9 tests passed (0.98s)
Test suite giter-test: PASS
Test suite logged to:
/home/refaelsh/repos/giter/dist-newstyle/build/x86_64-linux/ghc-9.2.7/giter-0.1.0/test/giter-0.1.0-giter-test.log
Error: .cabal-wrapped: Test coverage is only supported for packages with a
library component.
Error: cabal: Tests failed for giter-0.1.0.
I think the underlying technical problem is covered in this issue. Cabal assumes that you don't want a coverage report on the unit test code itself, just the code being tested, so it invokes hpc
in such a way that unit test code is excluded and only the source code actually being tested is included in coverage reporting. This is easy to do if the code is part of a separately compiled library: the library is covered, and any modules that are part of the unit testing are not covered.
However, if you have no library
stanza, and only executable
and test-suite
stanzas that use a shared set of modules, there's no obvious way to distinguish the test-suite
modules that are part of the unit testing and should not be covered from the modules that are part of the code being tested that should be covered.
More generally, Cabal has poor support for testing an executable
stanza. As per the discussion in this issue, the only way of actually testing an executable
is to use a hack -- either specify executable
and test-suite
stanzas that share the same hs-source-dirs
which ends up double-compiling everything, or else move everything important into a library
and leave your executable
as a stub. You are probably using the former approach, rather than the latter, but both kind of suck. The developers seem to have decided that the "move everything into a library
" is the recommended approach, even if not all of the documentation and/or tutorial material makes this clear.
Note that the situation for Stack is similar -- the default templates typically have a stub application containing only a main
function with everything else moved into a library.
I guess I would recommend moving your entire application (not just the parts you want to test) into a library
, and writing a stub main function for your executable
like:
module Main where
import qualified Lib
main :: IO ()
main = Lib.main
with both the test-suite
and executable
depending on the library
.