@hackage test-karya0.0.3

Testing framework.

Some time before 2008 I needed to write tests for Haskell, specifically for what became https://github.com/elaforge/karya. At the time, I think HUnit existed, but it did stuff I didn't think I needed, such as manually arranging tests into a hierarchy, and didn't have stuff I thought I did, such as automatic test discovery, diffs, filename:linenumber for failed tests, etc. So I wrote my own.

Over the years some features became obsolete (e.g. ghc now supports filename:linenumber natively via call stacks), but I got used to this style of tests, so I decided to try to extract it for use in cabal projects.

Here's what I like:

  • Automatic test discovery. Tests are any functions named test_* in a module named *_test.hs. In Karya, I use shake to collect the tests, but in this cabal version, I borrowed the -pgmF technique from hspec.

  • Automatic test hierarchy. Tests are named by their module name, so you can run subsets by pattern matching on the name. You don't have to collect them into suites, and when running them you don't have to dig through the files to see what they named their suites.

  • Low syntax overhead tests. A test is any standalone function that calls test functions (like equal):

    module M_test where
    import EL.Test.Global
    import qualified M
    
    test_something = do
        let f = M.something
        equal (f 10) 20
        equal (f 20) 30
    

    Tests don't abort on failure, so you can put a bunch together with local definitions. There's no need to name each test case, the error message will tell you module and line number. Since test_something is a normal IO () function you can run it in ghci, which is how I mainly work.

  • Doesn't mess with stdout. A check, like the equal function, is any function that prints to stdout. A line starting with __-> is a failure. This was originally the simplest stupidest thing that could work, but over the last 10 years there was never a reason to change it, and it turns out to be convenient. Since test output is integrated into the stdout stream, it's in the right context with any debugging or logging.

  • Colored formatted diffs. When values don't match, they are pretty printed and differing parts highlighted.

  • Various other ad-hoc conveniences: equalFmt attaches a formatter so you can get higher level diffs, stringsLike for matching strings with patterns, equalf and ApproxEq typeclass for floating point, and quickcheck for quickcheck properties with formatted diffs.

how to use

See example, but the short version is:

  • Install library and test-karya-generate binary with cabal install.

  • Make a test module like M_test above.

  • Make RunTests.hs with {-# OPTIONS_GHC -F -pgmF test-karya-generate #-}

  • Add a test-suite stanza to the cabal file, like in example.

  • cabal test. You can rerun the tests with dist/build/test/test, run only M_test with dist/build/test/test M_test or pass --help for more options, including running tests in parallel.