nWave/skills/nw-pbt-jvm/SKILL.md
JVM property-based testing with jqwik, ScalaCheck, and ZIO Test frameworks
npx skillsauth add nwave-ai/nwave nw-pbt-jvmInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| 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 |
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)
@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
)
@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; }
}
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 } }
}
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))
)
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(_))
)
}
import zio.test._
test("sort preserves length") {
check(Gen.listOf(Gen.int)) { xs => assertTrue(xs.sorted.length == xs.length) }
}
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.
<!-- jqwik (Maven) -->
<dependency>
<groupId>net.jqwik</groupId><artifactId>jqwik</artifactId>
<version>1.8.0</version><scope>test</scope>
</dependency>
// ScalaCheck (build.sbt)
libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.17.0" % Test
// ZIO Test: "dev.zio" %% "zio-test" % "2.x" % Test
Arbitrary[T] for automatic derivationtesting
Acceptance test creation methodology for the DISTILL wave. Domain knowledge for the acceptance designer agent: port-to-port principle, prior wave reading, wave-decision reconciliation, graceful degradation, and document back-propagation.
development
Cross-agent collaboration protocols, workflow handoff patterns, and commit message formats for TDD/Mikado/refactoring workflows
development
Creates a phased roadmap.json for a feature goal with acceptance criteria and TDD steps. Use when planning implementation steps before execution.
testing
Acceptance test creation methodology for the DISTILL wave. Domain knowledge for the acceptance designer agent: port-to-port principle, prior wave reading, wave-decision reconciliation, graceful degradation, and document back-propagation.