nw-pbt-rust

Rust property-based testing with proptest, quickcheck, and bolero frameworks

322 stars

Best use case

nw-pbt-rust is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Rust property-based testing with proptest, quickcheck, and bolero frameworks

Teams using nw-pbt-rust should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/nw-pbt-rust/SKILL.md --create-dirs "https://raw.githubusercontent.com/nWave-ai/nWave/main/nWave/skills/nw-pbt-rust/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/nw-pbt-rust/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How nw-pbt-rust Compares

Feature / Agentnw-pbt-rustStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Rust property-based testing with proptest, quickcheck, and bolero frameworks

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# PBT Rust -- proptest, quickcheck, bolero

## Framework Selection

| Framework | Shrinking | Stateful | Choose When |
|-----------|-----------|----------|-------------|
| proptest | Integrated | No | Default choice. Most features, best shrinking. |
| quickcheck | Type-based | No | Simple properties, one generator per type sufficient |
| bolero | Engine-dependent | No | Want to switch between PBT and fuzzing engines |

**Default**: proptest. Supports multiple strategies per type without newtype wrappers, better shrinking.

## Quick Start (proptest)

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

proptest! {
    #[test]
    fn sort_preserves_length(ref v in prop::collection::vec(any::<i32>(), 0..100)) {
        let mut sorted = v.clone();
        sorted.sort();
        prop_assert_eq!(sorted.len(), v.len());
    }
}

// Run: cargo test
```

## Generator (Strategy) Cheat Sheet (proptest)

### Primitives
```rust
any::<i32>()                          // any i32
0..100i32                             // range (implements Strategy)
any::<f64>()
any::<String>()
any::<bool>()
any::<Vec<u8>>()                      // bytes
Just(42)                              // constant
```

### Collections
```rust
prop::collection::vec(any::<i32>(), 0..50)
prop::collection::vec(any::<i32>(), 1..=10)   // non-empty, max 10
prop::collection::hash_set(any::<i32>(), 0..20)
prop::collection::hash_map(any::<String>(), any::<i32>(), 0..20)
(any::<i32>(), any::<String>())       // tuple
```

### Combinators
```rust
// Union
prop_oneof![any::<i32>().prop_map(Value::Int), any::<String>().prop_map(Value::Str)]

// Map
any::<i32>().prop_map(|x| x * 2)     // even integers

// Filter
any::<i32>().prop_filter("positive", |x| *x > 0)
// Prefer: 1..i32::MAX

// FlatMap (dependent generation)
prop::collection::vec(any::<i32>(), 1..50)
    .prop_flat_map(|v| {
        let len = v.len();
        (Just(v), 0..len)
    })

// Regex-based strings
"[a-z]{1,10}"                        // implements Strategy
"[0-9]{3}-[0-9]{4}"                  // phone-like pattern
```

### Recursive
```rust
fn json_value() -> impl Strategy<Value = JsonValue> {
    let leaf = prop_oneof![
        Just(JsonValue::Null),
        any::<bool>().prop_map(JsonValue::Bool),
        any::<i64>().prop_map(JsonValue::Number),
    ];
    leaf.prop_recursive(8, 256, 10, |inner| prop_oneof![
        prop::collection::vec(inner.clone(), 0..10).prop_map(JsonValue::Array),
        prop::collection::hash_map(".*", inner, 0..10).prop_map(JsonValue::Object),
    ])
}
```

### Custom Strategy
```rust
#[derive(Debug, Clone)]
struct User { name: String, age: u8 }

fn user_strategy() -> impl Strategy<Value = User> {
    ("[a-z]{1,20}", 1..120u8)
        .prop_map(|(name, age)| User { name, age })
}

// Or derive Arbitrary
#[derive(Debug, Arbitrary)]
struct Point { x: i32, y: i32 }
```

## Generator Cheat Sheet (quickcheck)

```rust
// Implement Arbitrary for custom types
impl quickcheck::Arbitrary for Color {
    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
        *g.choose(&[Color::Red, Color::Green, Color::Blue]).unwrap()
    }
}

// Built-in Arbitrary for primitives, Vec, String, Option, Result, tuples
// Bounded generation via Gen::choose
impl quickcheck::Arbitrary for SmallInt {
    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
        SmallInt(*g.choose(&(0..=100).collect::<Vec<_>>()).unwrap())
    }
    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
        Box::new(self.0.shrink().map(SmallInt))
    }
}
```

## Quick Start (quickcheck)

```rust
use quickcheck::quickcheck;

quickcheck! {
    fn prop_reverse_involutory(xs: Vec<i32>) -> bool {
        let rev: Vec<_> = xs.iter().rev().rev().cloned().collect();
        rev == xs
    }
}
```

## Stateful Testing

Not natively supported by proptest or quickcheck. Manual pattern:

```rust
#[derive(Debug, Clone, Arbitrary)]
enum Command { Put(String, i32), Get(String), Delete(String) }

proptest! {
    #[test]
    fn store_matches_model(cmds in prop::collection::vec(any::<Command>(), 0..50)) {
        let mut store = MyStore::new();
        let mut model = HashMap::new();
        for cmd in cmds {
            match cmd {
                Command::Put(k, v) => { store.put(&k, v); model.insert(k, v); }
                Command::Get(k) => {
                    prop_assert_eq!(store.get(&k), model.get(&k).copied());
                }
                Command::Delete(k) => { store.delete(&k); model.remove(&k); }
            }
        }
    }
}
```

## Quick Start (bolero)

```rust
use bolero::check;

#[test]
fn sort_test() {
    check!().with_type::<Vec<i32>>().for_each(|v| {
        let mut sorted = v.clone();
        sorted.sort();
        assert_eq!(sorted.len(), v.len());
    });
}
// Requires: cargo install cargo-bolero (for fuzzing engines)
// Run with fuzzer: cargo bolero test sort_test --engine libfuzzer
// Run as PBT: cargo test (uses random engine)
```

bolero's value: same test runs with libfuzzer, honggfuzz, AFL, or Kani verifier.

## Test Runner Integration

```rust
// proptest: add to Cargo.toml
// [dev-dependencies]
// proptest = "1"
// Failures saved to proptest-regressions/ directory

// Configure in proptest.toml or ProptestConfig
proptest! {
    #![proptest_config(ProptestConfig::with_cases(10000))]
    #[test]
    fn my_prop(x in any::<i32>()) { /* ... */ }
}
```

## Unique Features (proptest)

- **Regex-based string generation**: `"[a-z]{1,10}"` as a strategy
- **Multiple strategies per type**: No newtype wrappers needed (unlike quickcheck)
- **Failure persistence**: Saves to `proptest-regressions/` files, auto-replays
- **prop_compose!**: Macro for composing strategies declaratively
- **Integrated shrinking**: Constraint-aware, avoids generating invalid values during shrink

Related Skills

nw-ux-web-patterns

322
from nWave-ai/nWave

Web UI design patterns for product owners. Load when designing web application interfaces, writing web-specific acceptance criteria, or evaluating responsive designs.

nw-ux-tui-patterns

322
from nWave-ai/nWave

Terminal UI and CLI design patterns for product owners. Load when designing command-line tools, interactive terminal applications, or writing CLI-specific acceptance criteria.

nw-ux-principles

322
from nWave-ai/nWave

Core UX principles for product owners. Load when evaluating interface designs, writing acceptance criteria with UX requirements, or reviewing wireframes and mockups.

nw-ux-emotional-design

322
from nWave-ai/nWave

Emotional design and delight patterns for product owners. Load when designing onboarding flows, empty states, first-run experiences, or evaluating the emotional quality of an interface.

nw-ux-desktop-patterns

322
from nWave-ai/nWave

Desktop application UI patterns for product owners. Load when designing native or cross-platform desktop applications, writing desktop-specific acceptance criteria, or evaluating panel layouts and keyboard workflows.

nw-user-story-mapping

322
from nWave-ai/nWave

User story mapping for backlog management and outcome-based prioritization. Load during Phase 2.5 (User Story Mapping) to produce story-map.md and prioritization.md.

nw-tr-review-criteria

322
from nWave-ai/nWave

Review dimensions and scoring for root cause analysis quality assessment

nw-tlaplus-verification

322
from nWave-ai/nWave

TLA+ formal verification for design correctness and PBT pipeline integration

nw-test-refactoring-catalog

322
from nWave-ai/nWave

Detailed refactoring mechanics with step-by-step procedures, and test code smell catalog with detection patterns and before/after examples

nw-test-organization-conventions

322
from nWave-ai/nWave

Test directory structure patterns by architecture style, language conventions, naming rules, and fixture placement. Decision tree for selecting test organization strategy.

nw-test-design-mandates

322
from nWave-ai/nWave

Four design mandates for acceptance tests - hexagonal boundary enforcement, business language abstraction, user journey completeness, walking skeleton strategy, and pure function extraction

nw-tdd-review-enforcement

322
from nWave-ai/nWave

Test design mandate enforcement, test budget validation, 5-phase TDD validation, and external validity checks for the software crafter reviewer