Generates **property-based tests** that use randomized input generation to validate invariants and contracts (rather than hand-picked examples). Triggers when the conversation involves: PBT frameworks (Hypothesis library for Python, fast-check for TypeScript, proptest for Rust, rapid for Go, RapidCheck for C++); concepts like invariants, contracts, round-trip symmetry, encode/decode, serialize/deserialize, generative testing, or shrinking; or requests to find edge cases that example-based tests miss — e.g., "find edge cases automatically", "test all possible inputs", "verify this property holds". Does NOT trigger for: writing regular example-based unit tests, debugging, CI/CD setup, UI/component testing, or integration/E2E testing. Identifies up to 7 property patterns (round-trip, idempotence, invariance, metamorphic, inverse, ordering, no-crash), designs input generators, writes property tests, and extracts regression tests from failures.
91
90%
Does it follow best practices?
Impact
94%
1.11xAverage score across 5 eval scenarios
Passed
No known issues
Dependency: cargo add --dev proptest
use proptest::prelude::*;any::<i32>() // Any i32
0..100i32 // Range: 0..99
prop::num::i64::ANY // Any i64
any::<f64>() // Any f64 (includes NaN, inf)
"[a-zA-Z]+" // String matching regex (use `prop::string::*` for more control)
"([0-9]{1,3}\\.){3}[0-9]{1,3}" // Regex-based string generation
any::<bool>() // true or false
proptest::option::of(any::<i32>()) // Option<i32>
proptest::collection::vec(any::<i32>(), 0..100) // Vec<i32>
proptest::collection::vec(any::<i32>(), 1..=10) // Sized vec
proptest::collection::hash_map("[a-z]+", any::<i32>(), 0..10) // HashMap
(any::<i32>(), any::<String>()) // Tuples (up to 10 elements)
prop_oneof![Just(0), Just(1)] // One of several constant values
prop::sample::select(&["a", "b"]) // Select from a sliceproptest! {
#[test]
fn test_sort_length_invariant(mut v: Vec<i32>) {
let original_len = v.len();
v.sort();
prop_assert_eq!(v.len(), original_len);
}
#[test]
fn test_sort_ordered(mut v: Vec<i32>) {
v.sort();
for i in 0..v.len().saturating_sub(1) {
prop_assert!(v[i] <= v[i + 1]);
}
}
}prop_assert! vs assert!Always use prop_assert! / prop_assert_eq! inside proptest! — they properly report the shrunken input on failure. Regular assert! panics without the nice failure output.
prop_assume!proptest! {
#[test]
fn test_division(a: i32, b: i32) {
prop_assume!(b != 0);
prop_assert_eq!(a / b * b + a % b, a); // Basic integer division property
}
}#[test]
fn sorted_list_properties() {
let strategy = proptest::collection::vec(any::<i32>(), 0..100);
// Run with default config
proptest!(|(v in strategy)| {
let mut sorted = v.clone();
sorted.sort();
prop_assert_eq!(sorted.len(), v.len());
});
}// Map
let even_ints = any::<i32>().prop_map(|x| x * 2);
// Filter
let nonzero = any::<i32>().prop_filter("must be nonzero", |x| *x != 0);
// And-then (chain dependent strategies)
let user_name = "[a-zA-Z0-9_]+"
.prop_map(|s: String| -> String { s })
.prop_filter("no empty names", |s| !s.is_empty());#[derive(Debug, Clone)]
struct User {
name: String,
age: u8,
}
fn user_strategy() -> impl Strategy<Value = User> {
("[a-zA-Z]+", 0..120u8).prop_map(|(name, age)| User { name, age })
}
proptest! {
#[test]
fn test_user_properties(user in user_strategy()) {
prop_assert!(!user.name.is_empty());
prop_assert!(user.age <= 150);
}
}// Per-test config
proptest! {
#![proptest_config = ProptestConfig { cases: 500, .. ProptestConfig::default() }]
#[test]
fn test_heavy(v: Vec<i32>) { ... }
}
// Or inline
#[test]
fn test_custom_config() {
let config = ProptestConfig { cases: 10, .. ProptestConfig::default() };
proptest!(config, |(v in any::<Vec<i32>>())| {
// fast check
});
}proptest saves the seed in the failure output:
Test failed: assertion failed: ...; minimal failing input: v = [3, 1, 2]
successes: 42, failures: 1, shrinks: 12Re-run with the same seed via environment:
PROPTEST_FACTOR=12345 cargo test <test_name>Or set seed in config:
ProptestConfig { seed: Some(42), .. ProptestConfig::default() }cargo test # All tests
cargo test test_sort # Specific test
PROPTEST_CASES=500 cargo test # Override iteration count