MyNixOS website logo
Description

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.hs files in immediate subdirectories (e.g. test/Foo/Spec.hs)
  • *Spec.hs files 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 not Main, the main function is omitted and only spec is exported.
  • --subdir-file=FILENAME — Set the filename to look for in subdirectories (default: Spec.hs). This is useful when other tooling globs on *Spec.hs and interferes with the subdirectory entry points. Only affects subdirectory lookup — co-located *Spec.hs discovery 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

Featurehspec-discoverhspec-discover-discover
DiscoveryRecursive *Spec.hsImmediate subdirs with configurable file (default Spec.hs) + co-located *Spec.hs
NamingAny file ending in Spec.hsConfigurable file in subdirs, or *Spec.hs in same directory
GroupingFlat list of specsOne 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.

Metadata

Version

1.0.0.0

Platforms (80)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    uefi
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-uefi
  • aarch64-windows
  • aarch64_be-none
  • arc-linux
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • sh4-linux
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-uefi
  • x86_64-windows