Property Testing
Property-based testing checks invariants across hundreds of generated inputs, catching the edge cases example-based tests miss. Testkit’s generators keep those inputs inside ranges Soroban actually accepts.
Setup
Enable the proptest feature:
[dev-dependencies]
soroban-testkit-generators = { version = "0.3.0", features = ["proptest"] }
proptest = "1"
Basic property test
#![allow(unused)]
fn main() {
use proptest::prelude::*;
use soroban_testkit_generators::strategies;
proptest! {
#[test]
fn balance_never_negative(amount in strategies::token_amount()) {
// All generated amounts are >= 0 and within realistic bounds
assert!(amount >= 0);
}
}
}
Combining with fixtures
#![allow(unused)]
fn main() {
proptest! {
#[test]
fn transfer_preserves_supply(
amount_a in strategies::token_amount(),
amount_b in strategies::token_amount(),
) {
let ctx = TestContextBuilder::new().with_users(2).build();
// ... initialize balances, perform transfers
// ... assert total supply unchanged
}
}
}
Every generated case builds a real Env and executes on the host. If the suite slows down, reduce cases with #![proptest_config(ProptestConfig::with_cases(64))] before you narrow the strategy ranges.
When to use property testing
Arithmetic
Supply conservation, no overflow, and rounding that stays consistent across amounts.
State machines
A valid state remains reachable after any sequence of operations, not just the one you scripted.
Access control
Unauthorized callers fail regardless of the parameters they present.
Boundaries
Zero amounts, maximum values and empty collections are generated rather than remembered.
Strategy reference: soroban-testkit-generators.