Skip to content
Back to skills

Proptest Testing

ASecurity

Authors property-based tests in Rust using proptest - wires the `proptest!` macro, defines strategies (`prop::collection::vec`, type-driven `any` strategies, regex-based string strategies), uses the strategy-per-value model (vs QuickCheck's per-type) for flexible composition, and exploits proptest's automatic shrinking + persistence of failed cases (regression test artifact). Use when a Rust codebase needs PBT - pairs especially well with parsers, serializers, and any function with a structur...

  • 10 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 12, 2026
testingpythonrustgotestingdebugginggitapi

Works with

  • api

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 12, 2026

npx -y skills add testland/qa --skill proptest-testing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Proptest Testing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Proptest Testing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/testland-proptest-testing/badge)](https://www.skillsdirectory.com/skills/testland-proptest-testing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: proptest-testing
description: "Authors property-based tests in Rust using proptest - wires the `proptest!` macro, defines strategies (`prop::collection::vec`, type-driven `any` strategies, regex-based string strategies), uses the strategy-per-value model (vs QuickCheck's per-type) for flexible composition, and exploits proptest's automatic shrinking + persistence of failed cases (regression test artifact). Use when a Rust codebase needs PBT - pairs especially well with parsers, serializers, and any function with a structured input domain."
---

# proptest-testing

## Overview

Per [proptest-readme][pt]:

[pt]: https://github.com/proptest-rs/proptest

> "**Proptest** is a property testing framework inspired by Python's
> Hypothesis. It automatically generates test inputs and shrinks
> failures to minimal cases."

> "**Strategy-based approach**: Unlike QuickCheck, generation is
> 'defined on a per-value basis instead of per-type,' enabling
> flexible composition." ([proptest-readme][pt])

The strategy-based design is the key differentiator from Rust's
`quickcheck` crate - proptest separates the generation strategy
from the type, so multiple strategies can produce the same type
without newtype wrappers.

> "Failure persistence: Failed test cases are saved for regression
> testing." ([proptest-readme][pt])

This means a failed property test produces a `proptest-regressions/`
file checked into git; future runs replay it before generating new
cases.

## When to use

- A Rust codebase has functions with structured inputs (parsers,
  serializers, validators, encoders).
- A bug fix needs property-level prevention (not just an example
  test).
- Round-trip / invariant properties hold
  (`decode(encode(x)) == x`, `sort(sort(x)) == sort(x)`).
- A refactor needs equivalence verification (new vs old produce
  same outputs for all inputs).

## Install

In `Cargo.toml`:

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

Note: per [proptest-readme][pt], "the crate mainly sees passive
maintenance" - the API is stable; new development is rare.

## Basic property

Per [proptest-readme][pt]:

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

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

The `proptest!` macro wraps a `#[test]`-style function. The
parameter declaration `s in "..."` says "generate `s` from the
regex strategy."

Per [proptest-readme][pt], one of proptest's distinguishing
features is **regex-based string generation**: `"[0-9]{4}-[0-9]{2}-[0-9]{2}"`
generates date-shaped strings without writing a custom strategy.

## Strategy catalog

| Strategy                             | Generates                              |
|--------------------------------------|----------------------------------------|
| `any::<T>()`                          | Any value of type `T` (uses default strategy) |
| `0..100i32`                           | Integers in range                       |
| `"[a-z]+"` (regex string)              | Strings matching regex                  |
| `prop::collection::vec(strategy, 0..10)` | Vec of length 0-10                  |
| `prop::collection::hash_map(k, v, 0..10)` | HashMap                          |
| `prop::collection::btree_set(s, 0..10)` | BTreeSet                              |
| `(s1, s2, s3)` (tuple)                 | Tuple of values                         |
| `Just(value)`                         | Constant                                |
| `prop_oneof![s1, s2, s3]`             | One of multiple strategies              |
| `s.prop_map(f)`                       | Transform via function                  |
| `s.prop_filter("reason", f)`          | Filter (with reason for shrinking)      |
| `s.prop_flat_map(f)`                  | Dependent generation                    |

For custom domain strategies with `prop_compose!` and the
round-trip / invariant / equivalence property patterns they feed,
see
[references/strategies-and-patterns.md](references/strategies-and-patterns.md).

## Configuration

```rust
proptest! {
    #![proptest_config(ProptestConfig {
        cases: 1000,                                  // default 256
        max_shrink_iters: 100,                       // default 1024
        ..ProptestConfig::default()
    })]

    #[test]
    fn expensive_property(x in any::<u64>()) {
        // ...
    }
}
```

Common config options:

| Field                  | Default | Use                                                  |
|------------------------|--------:|------------------------------------------------------|
| `cases`                |    256  | More for higher confidence; slower CI.               |
| `max_shrink_iters`     |   1024  | Cap shrinking time on slow tests.                    |
| `max_shrink_time`      |    0    | Time-based shrink cap (ms; 0 = unlimited).           |
| `verbose`              |    0    | 0 / 1 / 2 - verbosity level.                        |
| `failure_persistence`  |  enabled | Persist failed cases to `proptest-regressions/`.    |

## Failure persistence (the regression file)

When a property fails, proptest writes:

```
# proptest-regressions/my_module.txt
# Seeds for failure cases proptest has generated in the past.
# It is recommended to commit this file.
cc abc1234567890 # shrinks to s = "1900-02-30"
```

**Commit this file to git.** Future runs replay these seeds before
generating new cases - locks the regression in.

If the regression is no longer relevant (the bug was fixed via a
different code path that doesn't fail this case anymore), delete
the entry. Don't suppress failures; understand them first.

## CI integration

```yaml
- run: cargo test --workspace
```

That's it - proptest tests are regular `#[test]` functions wrapped
in the macro. Cargo's `--test-threads` and parallel test execution
work normally.

For deterministic CI, set the seed via env var:

```yaml
env:
  PROPTEST_SEED: 0xDEADBEEF
- run: cargo test
```

## How to use

1. Add `proptest = "1"` under `[dev-dependencies]` in `Cargo.toml`.
2. Identify a property that holds for all inputs (round-trip,
   invariant, or equivalence), not a single example.
3. Declare inputs in the `proptest! { #[test] fn ... (x in <strategy>) { ... } }`
   form; pick strategies from the catalog (regex strings, ranges,
   `prop::collection::*`).
4. For structured domain types, build a custom strategy with
   `prop_compose!` -
   [references/strategies-and-patterns.md](references/strategies-and-patterns.md).
5. Assert with `prop_assert!` / `prop_assert_eq!` (not vanilla
   `assert!`) so the shrinker sees the failure context.
6. Run `cargo test`; on failure, commit the generated
   `proptest-regressions/` file so the seed replays on later runs.
7. Tune `cases` / `max_shrink_iters` via `#![proptest_config(...)]`
   when a test is slow.

## Worked example

An `RgbColor` type exposes `to_hex()` and `from_hex()`; the team
claims `from_hex(c.to_hex()) == c` for every color.

1. Build a strategy for arbitrary colors with `prop_compose!`
   ([references/strategies-and-patterns.md](references/strategies-and-patterns.md)):

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

prop_compose! {
    fn rgb()(r in any::<u8>(), g in any::<u8>(), b in any::<u8>()) -> RgbColor {
        RgbColor { r, g, b }
    }
}
```

2. Assert the round-trip with `prop_assert_eq!`:

```rust
proptest! {
    #[test]
    fn hex_round_trip(c in rgb()) {
        prop_assert_eq!(RgbColor::from_hex(&c.to_hex()), c);
    }
}
```

3. `to_hex()` emits lowercase but `from_hex()` rejects it, so the
   run fails, proptest shrinks, and writes a regression file:

```
# proptest-regressions/color.txt
cc 1a2b3c4d # shrinks to c = RgbColor { r: 0, g: 0, b: 10 }
```

4. Commit `proptest-regressions/color.txt` (see Failure
   persistence). Fix `from_hex()` to accept lowercase; re-run
   `cargo test` and the property passes all 256 cases, replaying the
   saved seed first.

## Anti-patterns

| Anti-pattern                                                          | Why it fails                                                              | Fix |
|-----------------------------------------------------------------------|---------------------------------------------------------------------------|-----|
| `.gitignore proptest-regressions/`                                     | Loses regression artifacts; future runs may not catch the same bug.      | Commit the directory (Failure persistence). |
| Heavy `prop_filter` on broad strategies                                 | Slow generation; many cases discarded.                                  | Constrained strategies (Strategy catalog, e.g. `1u32..100u32` instead of `any().filter`). |
| `assert!` instead of `prop_assert!` inside the macro                    | Shrinker can't see the failure context; minimal case obscured.          | Always `prop_assert!` / `prop_assert_eq!` (strategies-and-patterns.md). |
| Bare `unwrap()` inside the property without context                     | Error message is unhelpful; shrinker can't aid debugging.                | Use `prop_assert!(result.is_ok())` or `let v = result.unwrap()` after asserting. |
| `cases = 100_000` for a slow test                                       | CI never finishes; team disables.                                       | Budget appropriate to test runtime. |
| Mocking external services inside the property                          | Mocks don't satisfy properties.                                         | Test pure functions; integration tests separate. |
| Random seed in CI                                                       | Failures hard to reproduce.                                              | `PROPTEST_SEED` env var (CI integration). |

## Limitations

- **Maintenance status.** Per [proptest-readme][pt], "the crate
  mainly sees passive maintenance." Expect bug fixes but not new
  features.
- **MSRV 1.84.** Older Rust toolchains can't use the latest version.
- **Shrinking on complex types is slow.** `max_shrink_iters` /
  `max_shrink_time` cap it.
- **Async testing requires manual integration.** No native
  `proptest!` for `async fn`; wrap with `tokio::test` and
  `block_on`.
- **No race-condition detection.** Unlike fast-check's `fc.scheduler`,
  proptest doesn't model concurrent interleavings out of the box.

## References

- [pt][pt] - proptest README: strategy-based approach,
  regex-string generation, failure persistence, comparison vs
  QuickCheck, MSRV, license.
- [references/strategies-and-patterns.md](references/strategies-and-patterns.md) -
  `prop_compose!` custom strategies and round-trip / invariant /
  equivalence property patterns.
- `hypothesis-testing` - Python
  sibling proptest is inspired by.
- `fast-check-testing`,
  `jqwik-testing`,
  `quickcheck-testing` - 
  per-language siblings.

Files in this skill

  • .tessl-plugin/plugin.json646 B
  • SKILL.md10.6 KB
  • references/strategies-and-patterns.md1.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.