# Proptest ties shrinking to each generated value and keeps failing cases in proptest-regressions

> Proptest is a Hypothesis-inspired property testing framework for Rust. Strategies define generation and shrinking per value, failing inputs are minimized, and the repository is a four-crate workspace in passive maintenance.

**proptest-rs/proptest** — Hypothesis-like property testing for Rust

- Repository: https://github.com/proptest-rs/proptest
- Stars: 2,279 · Forks: 239
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-13 · Updated: 2026-10-30 · Language: en
- Canonical page: https://hysenlabs.com/projects/proptest-rs-proptest

## Generation and shrinking attach to each value, not to each type

Proptest generates random inputs, runs your test, and when something fails it reduces the input to a minimal failing case. The part that sets it apart from QuickCheck is where the logic lives. Generation and shrinking are defined on a per-value basis instead of per-type, which the project says makes it more flexible and simplifies composition.

In practice, the thing on the right of `in` inside a `proptest!` block is a strategy for that one argument. You do not need a global trait implementation to say how a type of integers or strings gets generated. The same type can be generated differently in two tests.

The project names Hypothesis for Python as its inspiration, and describes itself as part of the QuickCheck family. It recommends using property tests to complement traditional unit tests, not to replace them: hand-picked inputs cover known edge cases and past bugs, while the property tests search for more complicated inputs.

## A date parser that passes five unit tests and still panics on Unicode

The Getting Started chapter builds one example, a parse_date function for YYYY-MM-DD strings that slices a &str by byte offsets. Five hand-written unit tests pass, yet the function still has a bug. The first property test asks only that the function should not crash:

```rust
use proptest::prelude::*;

proptest! {
    #[test]
    fn doesnt_crash(s in "\\PC*") {
        parse_date(&s);
    }
}
```

The string strategy is a regular expression. `\\PC*` generates arbitrary strings of non-control characters. The failure the framework reports is a panic: byte index 4 is not a char boundary, with a minimal failing input of "aAௗ0㌀0". That is shrinking at work. The raw failing string was longer, and the reported one is the smallest the framework could find.

The fix the guide picks is deliberately small: reject anything where `!s.is_ascii()` and return None, so slicing never splits a character.

## The proptest-regressions directory is meant to be committed

When a test fails, proptest writes a `proptest-regressions` directory at the top of the project. It holds files that correspond to source files with failing test cases. The project calls this failure persistence, and its instruction is to add them to source control:

```text
$ git add proptest-regressions
```

The reasoning is that a stored failing case is replayed on later runs, so a bug found once is checked again. The guide also tells you to copy the failing case into a traditional unit test, because it exposed a bug unlike anything tested before. In the date example that becomes test_unicode_gibberish, asserting that parse_date("aAௗ0㌀0") returns None.

The cost is repository noise. The directory is generated, and each failure you do not clean up leaves files for the team to review. The workspace Cargo.toml excludes proptest/test-persistence-location/* from the workspace, which shows the project keeps persistence-location test fixtures apart from its crates.

## Setting up the dev-dependency and the property that every valid date parses

Add proptest under dev-dependencies in Cargo.toml, using the version the guide shows:

```toml
[dev-dependencies]
proptest = "1.12.0"
```

The second property in the date example generates inputs that match the format:

```rust
proptest! {
    // snip...

    #[test]
    fn parses_all_valid_dates(s in "[0-9]{4}-[0-9]{2}-[0-9]{2}") {
        parse_date(&s).unwrap();
    }
}
```

The right-hand side of `in` is a regular expression, and `s` is drawn from strings that match it. The earlier test used `"\\PC*"` for arbitrary non-control characters, and this one generates only YYYY-MM-DD strings. The guide says the new test passes and moves on.

The shipped README is cut off after that, so the remaining property the guide builds toward is not shown in this text. The book at proptest-rs.github.io/proptest holds the longer introduction.

## Four crates in one workspace: core, derive, macro and state-machine

The repository root is a Cargo workspace with four members: proptest, proptest-derive, proptest-state-machine and proptest-macro. Each has its own directory, and book/ holds the documentation source.

The workspace manifest pins shared dependencies. Among them are rand at 0.10 with default features off, chacha20 0.10 with the rng feature, rand_xorshift 0.5, regex 1.0, regex-syntax 0.8, rusty-fork 0.3.0 and proptest-macro 0.6.0 by path. A few, such as message-io with tcp, udp and websocket features, and the x86 crate, appear in the manifest without any explanation in the README.

The README does not say what proptest-derive, proptest-state-machine or proptest-macro do. Their names hint at deriving strategies and testing state machines, and the related names people search for pair proptest with derive and state machine, but the README stays silent on them. Open each crate's own directory before building on one.

## MSRV 1.88, and a stated cap of seven stable releases behind current

The current minimum supported Rust version is 1.88. The policy is that the MSRV will not exceed the current stable release minus 7, and the README adds that in practice it may be lower. Changing the policy in a backwards incompatible way, for example to the current stable minus 1, would count as a breaking change and bring a major version bump such as 1.1 to 2.0.

For a team, the consequence is a toolchain floor that moves. A project pinned to an older compiler must pin an older proptest too, since a newer release can raise the MSRV within that window. Check the 1.88 figure against your rust-toolchain before you update the dev-dependency.

The repository also ships flake.nix and flake.lock for Nix users, an .envrc, rustfmt.toml, gen-docs-all.sh and prerelease-checks.sh. The README does not say how to use them.

## Passive maintenance and a release list that stops at 0.9.6

The README describes the crate as fairly close to feature-complete, with no substantial architectural changes in quite some time. It mainly sees passive maintenance. The changelog, linked at proptest/CHANGELOG.md, lists substantial historical changes, breaking and otherwise.

The recent releases visible on the repository page are 0.9.6 from 2020-04-11 and 0.9.5 from 2020-01-17. Yet the README's own install snippet uses proptest = "1.12.0", so the GitHub release list is not a reliable view of what is current. Use crates.io, which the README badges link to, for the real version.

The repository was last pushed on 2026-10-10 and is not archived, with Apache-2.0 declared as the license and LICENSE-APACHE and LICENSE-MIT in the root. Passive maintenance means new feature requests are unlikely to be a priority. If you need a strategy combinator that does not exist, you will probably write it yourself.

## Conclusion

Use proptest if you write Rust and want randomized inputs with automatic shrinking next to your unit tests, and you can commit the proptest-regressions directory. Pass on it if you expect an actively growing feature set, since the project describes itself as in passive maintenance. First check your toolchain against MSRV 1.88, then work through the parse_date example in the book.

## FAQ

### What is a proptest?

Proptest is a property testing framework for Rust in the QuickCheck family, inspired by Python's Hypothesis. It tests that properties of your code hold for arbitrary inputs and finds a minimal failing case.

### What is property testing and what is it used for?

Property testing checks that certain properties of code output or behavior hold for all inputs, which are generated automatically. A failing input is reduced to a minimal test case, and it complements hand-written unit tests.

### How does proptest compare with quickcheck for Rust?

Unlike QuickCheck, proptest defines generation and shrinking per value instead of per type. The project says this makes it more flexible and simplifies composition.

## Sources

- [Issues](https://github.com/proptest-rs/proptest/issues)
- [License: Apache-2.0](https://github.com/proptest-rs/proptest/blob/main/LICENSE)
- [proptest-rs/proptest on GitHub](https://github.com/proptest-rs/proptest)
- [README](https://github.com/proptest-rs/proptest/blob/main/README.md)
- [Releases](https://github.com/proptest-rs/proptest/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/proptest-rs-proptest
