Skip to content

What it is

proptest is a property-based testing library that generates random inputs to falsify stated properties, then shrinks any failure to a minimal case.

State a property that should hold for all inputs. proptest generates hundreds of cases, and on failure shrinks the input to the smallest reproduction.

Installation

cargo add --dev proptest

Getting started

The smallest useful thing you can do with it, and what each part means.

Round-trip properties
use proptest::prelude::*;

proptest! {
    #[test]
    fn serialise_round_trips(book in any::<Book>()) {
        let encoded = serde_json::to_string(&book)?;
        let decoded: Book = serde_json::from_str(&encoded)?;
        prop_assert_eq!(book, decoded);
    }

    #[test]
    fn slugify_is_url_safe(s in ".*") {
        let slug = slugify(&s);
        prop_assert!(slug.chars().all(|c| c.is_ascii_alphanumeric() || c == '-'));
    }
}
Round-trip properties are the highest-value place to start: encode-then-decode should return the original, and violations usually indicate a real data-loss bug.

Advanced usage

Where the library earns its place over a simpler alternative.

Constrained generators
// Generate only inputs that satisfy your domain's rules.
fn valid_book() -> impl Strategy<Value = Book> {
    (
        "[a-zA-Z ]{1,200}",
        1400u16..=2100,
        prop::collection::vec("[a-z]{1,10}", 0..5),
    ).prop_map(|(title, year, tags)| Book { title, year, tags })
}

proptest! {
    #![proptest_config(ProptestConfig { cases: 500, ..Default::default() })]

    #[test]
    fn accepts_all_valid_books(book in valid_book()) {
        prop_assert!(validate(&book).is_ok());
    }
}
Custom strategies keep generation inside the domain, so failures represent real bugs rather than inputs your program never has to accept.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Local file too many rejects
prop_assume is discarding most generated inputs. Write a strategy that only produces valid values.
A failure will not reproduce
Use the seed printed in the failure output, and keep the proptest-regressions file in version control.

Best practices

  • Start with round-trip and invariant properties — they find the most bugs per line written.
  • Write custom strategies rather than filtering with prop_assume, which discards cases and slows generation.
  • Commit the proptest-regressions files; they pin previously-found failures as permanent tests.
  • Use property tests alongside example tests, not instead of them.

Background

Why it exists, and what it was reacting to.

Modelled on Haskell's QuickCheck and Python's Hypothesis, proptest finds the edge cases example-based tests miss — and its shrinking turns a 400-element counterexample into the two-element one that actually matters.