There is a python library named Hypothesis, which I’d like to have in elixir, so i’m thinking about porting it. That library is a property testing framework with a somewhat different approach to generating random test data.
Basically, the only data it generates is sequences of bytes, and has strategies to turn those bytes into meaningful datatypes (like maps, lists, lists of lists, etc).
The test case generators draw random bytes (the number of bytes might depend on the bytes that came before). This is easy to do in Elixir, of course (there is a random number module, so I’ve got that covered).
But them during test case shrinking I have to shrink the sewuence of bytes and generate the test case from that sequence of bytes insead. It’s important to have a generic interface to extract bytes from either a random source or a fixed source (a binary I’ve already generated). That way I can just call get_bytes(n)
and have n
new (maybe random, maybe fixed) bytes to work with, independently of their source.
Python being python, Hypothesis seems to handle this by defining objects with internal state from which you can read bytes. Those objects can return either random data or fixed data that was put there at initialization time.
So, how to port this to Elixir?
The naive way is implementing a GetBytes
behavior and write a GenServer/Agent, that implements such behavior. Then, to generate the test cases, I use a random server that returns actual random data (while saving the data to later use). When I begin shrinking, I can just spawn a server with the right byte sequence as state and draw from there. That way, the rest of my code doesn’t need to know about where the bytes are coming from.
This seems very easy, and quite similar from the Python version. But on the other hand I don’t know if this is the best use case for a GenServer. It’s basically being used like a python object after all. But I can’t think of any other abstraction that allows me to keep drawing a sequence of bytes as if they were random, while being actually deterministic.