0

I am trying to write my own test framework and I can't even get started. Here is my directory structure:

 $ tree
.
├── prj1
│   ├── package.yaml
│   ├── src
│   │   └── Module.hs
│   └── test-dir
│       └── TheTest.hs
└── stack.yaml

And each of the files is:

stack.yaml

resolver: lts-16.11
packages:
  - prj1

package.yaml

name: prj1
version: 0.1
maintainer: fakedrake
category: Test

library:
  source-dirs: src
  exposed-modules: []
  dependencies:
      - base

tests:
  prj1-tests:
    main: TheTest.hs
    source-dirs: test-dir
    dependencies:
      - base

TheTest.hs

module TheTest () where


main :: IO ()
main = putStrLn "Test passes!"

Module.hs

module Module () where

But when I run stack test I get

$ stack test
prj1> configure (lib + test)
Configuring prj1-0.1...
prj1> build (lib + test)
Preprocessing library for prj1-0.1..
Building library for prj1-0.1..
[1 of 2] Compiling Module
[2 of 2] Compiling Paths_prj1
Preprocessing test suite 'prj1-tests' for prj1-0.1..
Building test suite 'prj1-tests' for prj1-0.1..
[1 of 2] Compiling Paths_prj1
[2 of 2] Compiling TheTest

<no location info>: error:
    output was redirected with -o, but no output will be generated
because there is no Main module.

--  While building package prj1-0.1 using:
      /my/home/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_mPHDZzAJ_3.0.1.0_ghc-8.8.3 --builddir=.stack-work/dist/x86_64-osx/Cabal-3.0.1.0 build lib:prj1 test:prj1-tests --ghc-options ""
    Process exited with code: ExitFailure 1
Progress 1/2
2
  • IIRC the module name of an executable has to be Main. So in TheTest.hs you should change module TheTest to module Main, and probably also expose the main entrypoint. Commented Jan 2, 2021 at 18:37
  • You can probably check the module name rules in the Haskell report. Commented Jan 2, 2021 at 18:38

1 Answer 1

1

The convention for HPack / Stack based projects is that the main entry point for the tests resides in a file test/Spec.hs

So just rename your module TheTest.hs to Spec.hs and also set main: Spec.hs in the tests: section of the package.yaml and everything should be fine.

You could also produce a minimal HPack / Stack based project scaffold including test-suite with stack new prj1 as a starting point.

The nice thing about Spec.hs as main entry point is that it allow to have automatic lookup of test cases.

To enable this just place a single line in Spec.hs:

{-# OPTIONS_GHC -F -pgmF hspec-discover #-}

This pragma will find all module that follow the naming convention

*Spec.hs

and then you can have individual Test module like ReverseSpec.hs:

module ReverseSpec where

import           Test.Hspec

-- `main` is here so that this module can be run from GHCi on its own.  It is
-- not needed for automatic spec discovery.
main :: IO ()
main = hspec spec

-- this is the actual entry point for tests
spec :: Spec
spec = do
  describe "reverse" $ do
    it "is inverse to itself" $
      property $ \str -> (reverse . reverse) str `shouldBe` (id str :: String)

If you really want to roll your own Test framework, you can edit your file TheTest.hs as follows:

  1. Without a module clause:
-- module TheTest () where

main :: IO ()
main = putStrLn "Test passes!"
  1. or with a modified module clause:
module Main where

main :: IO ()
main = putStrLn "Test passes!"

This will work with both cabal test and stack test.

Sign up to request clarification or add additional context in comments.

3 Comments

I intentionally used these names to make it clear that I am trying to build my own testing framework. As far as I can tell from the documentation what you mention are HSpec conventions rather than stack conventions. Am I mistaken?
If you really want to roll your own, you can just place module Main where at the top of your TheTest.hs file. (Of course it would be better to then also call the file "Main.hs", but it will work this way also...
Maybe it's worth having a look at github.com/NorfairKing/sydtest. That's quite a new testing framework which tries to incorporate many best practises from older approaches.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.