I’m excited to announce the first release of a test framework I’ve been musing about for over a year! Skeletest takes inspiration from pytest and jest, two test frameworks that IMO are some of the best test frameworks out there. Skeletest is batteries-included and opinionated, but it’s also extendable and hookable.
Skeletest features that no other Haskell test frameworks have:
Assert on values without a Show instance (example, snapshot)
P.con magic for checking predicates on fields in a data type, e.g. P.con User{name = P.eq "alice"} (example)
Markers to tag tests for selection from CLI or modify in hooks
For example, some Skeletest tests are marked as integration tests, which are skipped by default (e.g. not run in Hackage CI), and can be selected on CLI with @integration
Nicely formatted snapshot files for easy auditability (example)
Allow multiple snapshots in one test
Select tests by filepath
Test discovery comes out of the box
Built-in xfail/skip modifiers
How does it compare to existing test frameworks?
hspec
Can’t define custom flags
Can’t hook into test execution
No built-in support for golden tests. hspec-golden exists, but because hspec isn’t extendable, you can’t just add a --update flag, you have to run a separate hgold executable
tasty
Barebones framework, requires adding multiple libraries for unit testing, property testing, etc.
sydtest
Actually pretty solid, has a lot of nice features
Can’t define custom flags
Can’t hook into test execution
Built-in golden test
Fairly detailed failure messages, can’t do more complex predicates like explainable-predicates, though
Not a fan of random test order by default
Writing the tests for Skeletest itself has been such a pleasant experience, I’m really excited to see if that holds true in the wild as well. The project is only a month old, so it’s still rough and fresh, but I’m optimistic about what’s possible!
Note: To preempt some comments I know some of you will want to bring up — yes, this framework is intended to be the default test framework for a new build tool I’m experimenting with. I know people are very passionate about build tools and package managers and Cabal and Stack, and I do very much want to have that discussion, but let’s do that in a separate thread, after I flesh out my ideas a bit more.
Basically, skeletest uses both a preprocessor and GHC plugin. One of the things the plugin does is search for and replace P.con with an internal conMatches function before the typechecking phase.
This is fascinating! I’ve been a big fan of pytest, because it has so many features that make working with tests a dream. The idea of fixtures (and them being trees) imo is brilliant. Especially with included cleanup actions, they become very powerful.
It looks like you solved a big problem in pytest’s execution of fixtures: the fact that they’re implicit. Say you have a fixture called foo and want to use it in a test. The way you do it in pytest is by adding a parameter to the function named foo. Pytest magically figures out that you mean to use the fixture.
You solved it by demanding fixtures are explicitly retrieved by using the type class functions. Nice work!
When I have time, I’d love to take a closer look and see how this compares to pytest. Pytest has a billion features that I cannot expect a new project to instantly have, but from reading the README it looks very promising!
The preprocessor is already a requirement in order to do test discovery, so we have that regardless, and the plugin makes it easier to generate the main function than the preprocessor, so it’s already gonna be registered without this.
Plus, I want to do other things like show the function name in the failure message of P.>>>, which requires either a plugin or TH.
Thanks for your work on this! I just ported a library from sydtest to skeletest to give it a spin. I really like how easy the fixtures are! The test output is nice too. The custom predicates were straightfoward to figure out (I had to hunt down (<<<)). It runs a little bit slower than sydtest I think. I’ll stick with it because I like the fixtures so much!