nw-pbt-jvm

JVM property-based testing with jqwik, ScalaCheck, and ZIO Test frameworks

322 stars

Best use case

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

JVM property-based testing with jqwik, ScalaCheck, and ZIO Test frameworks

Teams using nw-pbt-jvm 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-jvm/SKILL.md --create-dirs "https://raw.githubusercontent.com/nWave-ai/nWave/main/nWave/skills/nw-pbt-jvm/SKILL.md"

Manual Installation

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

How nw-pbt-jvm Compares

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

Frequently Asked Questions

What does this skill do?

JVM property-based testing with jqwik, ScalaCheck, and ZIO Test 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 JVM -- jqwik (Java/Kotlin) + ScalaCheck + ZIO Test

## Framework Selection

| Framework | Language | Shrinking | Stateful | Choose When |
|-----------|----------|-----------|----------|-------------|
| jqwik | Java/Kotlin | Integrated | Yes (actions) | Java/Kotlin projects (recommended default) |
| ScalaCheck | Scala | Type-based | Yes (Commands) | Scala projects (established choice) |
| ZIO Test | Scala | Integrated | Via effects | ZIO-based Scala projects |

## Quick Start (jqwik)

```java
import net.jqwik.api.*;

class SortProperties {
    @Property
    void sortPreservesLength(@ForAll List<Integer> list) {
        List<Integer> sorted = new ArrayList<>(list);
        Collections.sort(sorted);
        Assertions.assertEquals(list.size(), sorted.size());
    }
}
// Run: ./gradlew test (or mvn test)
```

## Generator (Arbitrary) Cheat Sheet (jqwik)

```java
@ForAll int x                          // any int
@ForAll @IntRange(min = 0, max = 99) int x
@ForAll @StringLength(min = 1, max = 50) String s
@ForAll @Size(min = 1, max = 10) List<Integer> list

// Custom provider
@Provide
Arbitrary<String> emails() {
    return Arbitraries.strings().alpha().ofMinLength(1).ofMaxLength(10)
        .map(name -> name + "@example.com");
}

// Combinators
Arbitraries.integers().between(0, 100)
Arbitraries.of("a", "b", "c")
Arbitraries.frequencyOf(Tuple.of(80, Arbitraries.integers()), Tuple.of(20, Arbitraries.just(0)))

// Combine
Combinators.combine(
    Arbitraries.strings().alpha().ofMinLength(1),
    Arbitraries.integers().between(1, 120)
).as((name, age) -> new User(name, age))

// Recursive
Arbitraries.recursive(
    () -> Arbitraries.of(JsonValue.NULL, JsonValue.TRUE),
    inner -> Arbitraries.maps(Arbitraries.strings(), inner).map(JsonValue::fromMap),
    5
)
```

## Stateful Testing (jqwik)

```java
@Property
void storeMatchesModel(@ForAll("storeActions") ActionSequence<MyStore> actions) {
    actions.run(new MyStore());
}

@Provide
ActionSequenceArbitrary<MyStore> storeActions() {
    return Arbitraries.sequences(
        Arbitraries.oneOf(
            Combinators.combine(Arbitraries.strings(), Arbitraries.integers())
                .as(PutAction::new),
            Arbitraries.strings().map(GetAction::new)
        )
    );
}

class PutAction implements Action<MyStore> {
    final String key; final int value;
    PutAction(String key, int value) { this.key = key; this.value = value; }
    @Override public MyStore run(MyStore store) { store.put(key, value); return store; }
}
```

## Quick Start (ScalaCheck)

```scala
import org.scalacheck.Prop.forAll

val propSortLength = forAll { (xs: List[Int]) => xs.sorted.length == xs.length }

// With ScalaTest
class SortSpec extends AnyFunSuite with ScalaCheckPropertyChecks {
  test("sort idempotent") { forAll { (xs: List[Int]) => xs.sorted.sorted shouldBe xs.sorted } }
}
```

### ScalaCheck Generators
```scala
Gen.choose(0, 100)                    // bounded int
Gen.alphaStr                          // alphabetic string
Gen.listOf(Gen.posNum[Int])           // list
Gen.oneOf(Gen.const(1), Gen.const(2)) // union
Gen.frequency((80, Gen.alphaChar), (20, Gen.numChar))
Gen.recursive[Tree](gen =>
  Gen.oneOf(Gen.const(Leaf), for { l <- gen; r <- gen; v <- Gen.posNum[Int] } yield Node(v, l, r))
)
```

### ScalaCheck Stateful (Commands)
```scala
object StoreSpec extends Commands {
  type State = Map[String, Int]; type Sut = MyStore
  def genCommand(state: State): Gen[Command] = Gen.oneOf(
    for { k <- Gen.alphaStr; v <- Gen.posNum[Int] } yield Put(k, v),
    Gen.oneOf(state.keys.toSeq).map(Get(_))
  )
}
```

## Quick Start (ZIO Test)

```scala
import zio.test._

test("sort preserves length") {
  check(Gen.listOf(Gen.int)) { xs => assertTrue(xs.sorted.length == xs.length) }
}
```

### ZIO Test Generator Cheat Sheet
```scala
Gen.int                               // any Int
Gen.int(0, 100)                       // bounded
Gen.double                            // any Double
Gen.string                            // any String
Gen.alphaNumericString
Gen.boolean
Gen.listOf(Gen.int)                   // List[Int]
Gen.setOf(Gen.string)                 // Set[String]
Gen.mapOf(Gen.string, Gen.int)        // Map[String, Int]
Gen.option(Gen.int)                   // Option[Int]
Gen.oneOf(Gen.const(1), Gen.const(2)) // union
Gen.weighted((Gen.int, 80.0), (Gen.const(0), 20.0))  // weighted

// Custom
val genUser = for {
  name <- Gen.alphaNumericString
  age  <- Gen.int(1, 120)
} yield User(name, age)
```

ZIO Test stateful testing: Use `ZIO.stateful` with Ref-based model state in effect composition.

## Test Runner Integration

```xml
<!-- jqwik (Maven) -->
<dependency>
    <groupId>net.jqwik</groupId><artifactId>jqwik</artifactId>
    <version>1.8.0</version><scope>test</scope>
</dependency>
```
```scala
// ScalaCheck (build.sbt)
libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.17.0" % Test
// ZIO Test: "dev.zio" %% "zio-test" % "2.x" % Test
```

## Unique Features

### jqwik
- **Edge cases**: Automatically tests boundary values (0, MIN/MAX, empty)
- **@StatisticsReport**: Shows distribution of generated values
- **Domains**: Group related arbitraries into reusable contexts
- **Kotlin support**: Works natively via JUnit 5

### ScalaCheck
- **Type-class based**: `Arbitrary[T]` for automatic derivation
- **Parallel Commands**: Stateful testing with parallel execution
- **Shrink[T]**: Separate shrink type class (can shrink past generator constraints)

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