Hello. I wrote Hypothesis. Then, back in November, I joined Antithesis, shortly followed by Liam DeVoe (another core Hypothesis maintainer). The inevitable result was synthesis, which is why today we’re introducing our new family of property-based testing libraries, Hegel.1 It's a philosophy joke.
Hegel is an attempt to bring the quality of property-based testing found in Hypothesis to every language, and to make this seamlessly integrate with Antithesis to increase its bug-finding power. Today we’re releasing Hegel for Rust, but this is the first of many libraries. We plan to release Hegel for Go in the next week or two, and we’ve got Hegel libraries in various states of readiness for C++, OCaml, and TypeScript that we plan to release over the coming weeks or months.
Here’s an example from Hegel for Rust to whet your appetite:
#[ hegel :: test ( test_cases = 1000 )] fn test_fraction_parse_robustness ( tc : hegel :: TestCase ) { let s : String = tc . draw ( generators :: text ()); let _ = Fraction :: from_str (& s ); // should never panic }
This finds a bug in the fraction crate where from_str("0/0") panics rather than returning an error value.2 If that was already enough of a sales pitch for you, you can check out Hegel here. If not, let me tell you a bit more about why property-based testing, and Hegel in particular, are pretty great and why I think you should use them. Several of the bugs mentioned in this post are from libraries that don't seem actively maintained. This isn't really a deliberate choice, and I don't want to pick on the authors, it's just way easier to find nice illustrative bugs in such libraries.
We saw an example of it above with Hegel for Rust: Property-based testing is testing where, rather than providing a full concrete test case yourself, you instead use the library to specify a range of values for which the test should pass. In our fraction example, our claim was a common one: Our parser should never crash, it should always either produce a valid result or error value.
You can think of that property-based test as infinitely many copies of tests that look like the following, where each test replaces the s value with a different string:
#[ test ] fn test_fraction_parse_robustness () { let s : String = " 0/0 " ; let _ = Fraction :: from_str (& s ); // should never panic }
The benefit of property-based testing libraries is that you don’t have to come up with those strings.
“Doesn’t crash” is probably the most boring property-based test, but it’s surprisingly useful. Coming from Python, it’s very useful (it’s surprisingly hard to write a Python program that never crashes), but as we saw, this happens even in Rust.
... continue reading