Please see the README on GitHub at https://github.com/parsonsmatt/hspec-discover-discover#readme.
Please see the README on GitHub at https://github.com/parsonsmatt/hspec-discover-discover#readme
hspec-discover-discover
A GHC preprocessor for hspec that discovers test modules in immediate subdirectories and co-located *Spec.hs files.
Unlike hspec-discover, which recursively finds all *Spec.hs files, hspec-discover-discover looks for:
Spec.hsfiles in immediate subdirectories (e.g.test/Foo/Spec.hs)*Spec.hsfiles in the same directory as the entry point (e.g.test/FooSpec.hs)
This gives you explicit control over test organization — each subdirectory or spec file is a top-level test group.
Usage
In your test/Spec.hs:
{-# OPTIONS_GHC -F -pgmF hspec-discover-discover #-}
Then organize your tests as subdirectories containing Spec.hs, or as *Spec.hs files alongside the entry point:
test/
Spec.hs -- preprocessor entry point (the line above)
FooSpec.hs -- module FooSpec, exports spec :: Spec
ParseArgs/
Spec.hs -- module ParseArgs.Spec, exports spec :: Spec
Discover/
Spec.hs -- module Discover.Spec, exports spec :: Spec
Generate/
Spec.hs -- module Generate.Spec, exports spec :: Spec
Each module should export a spec :: Spec:
module ParseArgs.Spec (spec) where
import Test.Hspec
spec :: Spec
spec = do
it "does something" $ do
True `shouldBe` True
This file can itself be generated from hspec-discover, though you will need this patch for it to generate modules properly in subdirectories.
Generated output
For the directory structure above, hspec-discover-discover generates:
{-# LINE 1 "test/Spec.hs" #-}
module Main (main) where
import Test.Hspec
import qualified Discover.Spec
import qualified Generate.Spec
import qualified ParseArgs.Spec
import qualified FooSpec
main :: IO ()
main = hspec spec
spec :: Spec
spec = do
describe "Discover" Discover.Spec.spec
describe "Generate" Generate.Spec.spec
describe "ParseArgs" ParseArgs.Spec.spec
describe "Foo" FooSpec.spec
Options
Pass options via -optF in your GHC options:
--module-name=NAME— Set the generated module name (default:Main). When the module name is notMain, themainfunction is omitted and onlyspecis exported.--subdir-file=FILENAME— Set the filename to look for in subdirectories (default:Spec.hs). This is useful when other tooling globs on*Spec.hsand interferes with the subdirectory entry points. Only affects subdirectory lookup — co-located*Spec.hsdiscovery is unchanged.
For example, to look for SubTest.hs instead of Spec.hs in subdirectories:
{-# OPTIONS_GHC -F -pgmF hspec-discover-discover -optF --subdir-file=SubTest.hs #-}
This would discover test/Foo/SubTest.hs and generate import qualified Foo.SubTest.
Comparison with hspec-discover
| Feature | hspec-discover | hspec-discover-discover |
|---|---|---|
| Discovery | Recursive *Spec.hs | Immediate subdirs with configurable file (default Spec.hs) + co-located *Spec.hs |
| Naming | Any file ending in Spec.hs | Configurable file in subdirs, or *Spec.hs in same directory |
| Grouping | Flat list of specs | One describe per subdirectory or local spec |
Truly, these tools are meant to be used in conjunction with each other. hspec-discover-discover is primarily useful when hspec-discover's single generated Spec.hs module becomes too large to compile quickly. This tool was developed when I noticed that our Spec.hs module was taking 2:36 to compile. Splitting things up into test/Spec.hs with this tool (2s compile time) and a myriad of test/*/TestGroup.hs files (can compile in parallel much earlier in the build graph) dropped our overall CI build time from 8:00 to 6:30.
License
BSD-3-Clause.