GitHub - shrynx/protest: An ergonomic, powerful, and feature-rich property

⚓ Rust    📅 2025-11-01    👤 surdeus    👁️ 12      

surdeus

I wrote a library for property based testing in rust. I'll post part of the readme here for ease

Property-Based Testing for Rust - An ergonomic, powerful, and feature-rich property testing library with minimal boilerplate.

Features

  • :rocket: Ergonomic API - Test properties with closures, no boilerplate
  • :bullseye: Automatic Generator Inference - Smart type-based generator selection
  • :wrench: Derive Macros - #[derive(Generator)] for custom types
  • :package: Declarative Macros - property!, assert_property!, generator!
  • :high_voltage: Async Support - First-class async property testing
  • :counterclockwise_arrows_button: Smart Shrinking - Automatic minimal counterexample finding
  • :floppy_disk: Failure Persistence - Save and replay failing test cases (optional)
  • :wrench: CLI Tool - Manage failures from the command line (protest-cli)
  • :artist_palette: Fluent Builders - Chain configuration methods naturally
  • :test_tube: Common Patterns - Built-in helpers for mathematical properties
  • :shuffle_tracks_button: Parallel Execution - Run tests in parallel for speed
  • :bar_chart: Statistics & Coverage - Track generation and test coverage
  • :performing_arts: Flexible - Works with any type, sync or async

Ultra-Simple Example

use protest::*;

#[test]
fn test_addition_commutative() {
    // Test that addition is commutative with just one line!
    property!(generator!(i32, -100, 100), |(a, b)| a + b == b + a);
}

Ergonomic API Example

use protest::ergonomic::*;

#[test]
fn test_reverse_twice_is_identity() {
    property(|mut v: Vec<i32>| {
        let original = v.clone();
        v.reverse();
        v.reverse();
        v == original
    })
    .iterations(1000)
    .run_with(VecGenerator::new(IntGenerator::new(-50, 50), 0, 100))
    .expect("Property should hold");
}

Attribute Macro Example

use protest::property_test;

#[property_test(iterations = 100)]
fn test_string_length(s: String) {
    // Generator automatically inferred from type
    assert!(s.len() >= 0);
}

Custom Struct Example

use protest::Generator;

#[derive(Debug, Clone, PartialEq, Generator)]
struct User {
    #[generator(range = "1..1000")]
    id: u32,

    #[generator(length = "5..50")]
    name: String,

    age: u8,
    active: bool,
}

#[property_test]
fn test_user_id(user: User) {
    assert!(user.id > 0 && user.id < 1000);
}

1 post - 1 participant

Read full topic

🏷️ Rust_feed