unit-testinghaskellhspec

hspec defined tests invoked with stack throw an error when test file is defined as a module


I'm trying to get my head around the reason why the test file containing unit-tests which is defined as a module fails when run with stack build --test.

Say having a simple test module defined from scratch with:

stack new test-module
cd test-module

vim package.yaml # remove "executables" section, add "hspec" as tests dependency

Following "getting started" instructions from Hspec documentation I've modified files such as:

Step 1: Describe your desired behavior

-- file test/Spec.hs

module LibSpec where

import Test.Hspec
import Lib

main :: IO ()
main = hspec $ do
    describe "divides" $ do
        it "returns True when the first number divides the second" $
           2 `divides` 4 `shouldBe` True

Step 2: Write some code

-- file src/Lib.hs

module Lib (divides) where

divides :: Integer -> Integer -> Bool
divides d n = rem n d == 0

Running stack build --test throws the following error:

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

When I comment out the "module definition" line from the test/Spec.hs file the build succeeds and the unit-test passes:

-- file test/Spec.hs

-- Notice the next line is commented out:
-- module LibSpec where

import Test.Hspec
import Lib

main :: IO ()
main = hspec $ do
    describe "divides" $ do
        it "returns True when the first number divides the second" $
           2 `divides` 4 `shouldBe` True

Is that Hspec related or Stack related? Or maybe am I missing something obvious?


Solution

  • It's part of Haskell the language.

    A Haskell program is a collection of modules, one of which, by convention, must be called Main and must export the value main.

    An abbreviated form of module, consisting only of the module body, is permitted. If this is used, the header is assumed to be module Main(main) where.


    The Haskell 2010 report, section 5 (Modules) https://www.haskell.org/onlinereport/haskell2010/haskellch5.html#x11-980005


    See also the cabal documentation, about the configuration that package.yaml is a proxy for, the field containing the test/executable file:

    main-is: (...) while the name of the file may vary, the module itself must be named Main.


    https://www.haskell.org/cabal/users-guide/developing-packages.html#pkg-field-executable-main-is


    GHC has an option -main-is MyModule.mymain to override this behavior (documented in the GHC user guide).