I recently started a discussion about the state and future of a library that I like a lot, smallcheck. The maintainer argues that smallcheck is “largely obsolete” in the presence of libraries like falsify. I don’t want to repeat the discussion, if you have an opinion it is warmly welcome at the linked thread. I only mention it because it sparked my interest in the topic.
I toyed around and came up with a very simple way to write an enumeration based property testing library. The core ideas are:
- Generators are just streams of values, ordered by complexity (some measure of “size”) of the data.
- Generators are combined fairly by interleaving:
interleave :: [a] -> [a] -> [a]
interleave [] as = as
interleave (a : as1) as2 = a : interleave as2 as1
Idea 1 is the basic tenet of enumeration based property testing and has been studied in the literature and is applied in many libraries. No need for random number generation and RNG seeding, no need for shrinking. As a bonus, testing is deterministic.
Idea 2 just makes it really simple to write generators for any kind of algebraic datatype. Generic instances for Arbitrary are fast to write and useful. All kinds of shapes of your data show up early in the test. In contrast to smallcheck, no concept of depth is needed. Generation is very fast and uses little memory. If your test situation needs very specific test data, just interleave it with the standard generator.
I wrapped all of this up and fleshed it out with a bit of AI help (but I checked everything thoroughly). The result is a small library with very few dependencies. There is tasty integration. There are a few examples. So far it seems to work really well. If you are looking for a tiny property testing library, have a look at tinycheck, and let me know what you think.