Comprehensive property-based testing with generators, automatic shrinking, and configurable test execution.
Functions for running property-based tests with different execution strategies.
/**
* Tests a property with random samples from generator
* @param rv - Generator for test values
* @param test - Property test function
* @returns Test result aggregating all sample results
*/
def check[R <: TestConfig, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult]
/**
* Tests an effectful property with random samples
* @param rv - Generator for test values
* @param test - Effectful property test function
* @returns ZIO effect producing aggregated test result
*/
def checkM[R <: TestConfig, R1 <: R, E, A](rv: Gen[R, A])(test: A => ZIO[R1, E, TestResult]): ZIO[R1, E, TestResult]
/**
* Tests a property exhaustively with all samples from generator
* @param rv - Generator for test values (should be finite)
* @param test - Property test function
* @returns Test result for all generated values
*/
def checkAll[R <: TestConfig, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult]
/**
* Tests an effectful property exhaustively
* @param rv - Generator for test values (should be finite)
* @param test - Effectful property test function
* @returns ZIO effect producing complete test result
*/
def checkAllM[R <: TestConfig, R1 <: R, E, A](rv: Gen[R, A])(test: A => ZIO[R1, E, TestResult]): ZIO[R1, E, TestResult]
/**
* Tests a property with specific number of samples
* @param n - Number of samples to generate
* @returns CheckN instance for chaining with generator and test
*/
def checkN(n: Int): CheckVariants.CheckN
/**
* Tests an effectful property with specific number of samples
* @param n - Number of samples to generate
* @returns CheckNM instance for chaining with generator and test
*/
def checkNM(n: Int): CheckVariants.CheckNMThe main generator class with monadic operations and transformations.
/**
* A generator that produces values of type A in environment R
* @tparam R - Environment requirements (usually TestConfig or Sized)
* @tparam A - Type of generated values
*/
abstract class Gen[-R, +A] {
/** Transform generated values with a function */
def map[B](f: A => B): Gen[R, B]
/** Monadic composition of generators */
def flatMap[R1 <: R, B](f: A => Gen[R1, B]): Gen[R1, B]
/** Filter generated values based on predicate */
def filter(f: A => Boolean): Gen[R, A]
/** Enable for-comprehension syntax */
def withFilter(f: A => Boolean): Gen[R, A]
/** Combine with another generator producing tuples */
def zip[R1 <: R, B](that: Gen[R1, B]): Gen[R1, (A, B)]
/** Combine generators with transformation function */
def zipWith[R1 <: R, B, C](that: Gen[R1, B])(f: (A, B) => C): Gen[R1, C]
/** Get stream of samples for testing */
def sample: ZStream[R, Nothing, Sample[R, A]]
/** Disable shrinking for this generator */
def noShrink: Gen[R, A]
/** Make generator produce optional values */
def optional: Gen[R, Option[A]]
/** Set specific size for generation */
def resize(size: Int): Gen[R, A]
/** Set size using an effect */
def resizeM[R1 <: R](size: ZIO[R1, Nothing, Int]): Gen[R1, A]
}Fundamental generators for creating constant and computed values.
/** Generator that always produces unit value */
val unit: Gen[Any, Unit]
/** Generator that produces constant value */
def const[A](a: => A): Gen[Any, A]
/** Generator that produces pure value */
def succeed[A](a: A): Gen[Any, A]
/** Generator that produces lazy value */
def succeedLazy[A](a: => A): Gen[Any, A]
/** Generator that produces failure */
def fail[E](error: E): Gen[Any, E]
/** Generator that throws exception */
def failingWith[A](gen: Gen[Any, Throwable]): Gen[Any, A]Built-in generators for primitive Scala types.
/** Boolean generator */
val boolean: Gen[Any, Boolean]
/** Byte generator with size-based range */
val byte: Gen[Sized, Byte]
/** Short generator with size-based range */
val short: Gen[Sized, Short]
/** Int generator with size-based range */
val int: Gen[Sized, Int]
/** Long generator with size-based range */
val long: Gen[Sized, Long]
/** Float generator */
val float: Gen[Sized, Float]
/** Double generator */
val double: Gen[Sized, Double]
/** BigInt generator */
val bigInt: Gen[Sized, BigInt]
/** BigDecimal generator */
val bigDecimal: Gen[Sized, BigDecimal]
/** Character generator */
val char: Gen[Any, Char]
/** String generator with size-based length */
val string: Gen[Sized, String]Generators that produce values within specified ranges.
/** Int generator within range */
def int(min: Int, max: Int): Gen[Any, Int]
/** Long generator within range */
def long(min: Long, max: Long): Gen[Any, Long]
/** Double generator within range */
def double(min: Double, max: Double): Gen[Any, Double]
/** Float generator within range */
def float(min: Float, max: Float): Gen[Any, Float]
/** BigInt generator within range */
def bigInt(min: BigInt, max: BigInt): Gen[Any, BigInt]
/** BigDecimal generator within range */
def bigDecimal(min: BigDecimal, max: BigDecimal): Gen[Any, BigDecimal]
/** Character generator within range */
def char(min: Char, max: Char): Gen[Any, Char]Specialized generators for different character sets.
/** Alphabetic characters (a-z, A-Z) */
val alphaChar: Gen[Any, Char]
/** Alphanumeric characters (a-z, A-Z, 0-9) */
val alphaNumericChar: Gen[Any, Char]
/** Lowercase alphabetic characters (a-z) */
val alphaLowerChar: Gen[Any, Char]
/** Uppercase alphabetic characters (A-Z) */
val alphaUpperChar: Gen[Any, Char]
/** ASCII characters */
val asciiChar: Gen[Any, Char]
/** Hexadecimal characters (0-9, a-f, A-F) */
val hexChar: Gen[Any, Char]
/** Numeric characters (0-9) */
val numericChar: Gen[Any, Char]
/** Printable characters */
val printableChar: Gen[Any, Char]Generators for strings with various constraints.
/** Non-empty string generator using character generator */
def string1(charGen: Gen[Any, Char]): Gen[Sized, String]
/** String generator using specific character generator */
def stringGen(charGen: Gen[Any, Char]): Gen[Sized, String]
/** String of fixed length */
def stringN(n: Int): Gen[Any, String]
/** String within length bounds */
def stringBounded(min: Int, max: Int): Gen[Any, String]Generators that produce collections of values.
/** List generator with size-based length */
def listOf[R, A](gen: Gen[R, A]): Gen[R with Sized, List[A]]
/** Non-empty list generator */
def listOf1[R, A](gen: Gen[R, A]): Gen[R with Sized, ::[A]]
/** List generator with fixed size */
def listOfN[R, A](n: Int)(gen: Gen[R, A]): Gen[R, List[A]]
/** List generator with bounded size */
def listOfBounded[R, A](min: Int, max: Int)(gen: Gen[R, A]): Gen[R, List[A]]
/** Vector generator with size-based length */
def vectorOf[R, A](gen: Gen[R, A]): Gen[R with Sized, Vector[A]]
/** Non-empty vector generator */
def vectorOf1[R, A](gen: Gen[R, A]): Gen[R with Sized, Vector[A]]
/** Vector generator with fixed size */
def vectorOfN[R, A](n: Int)(gen: Gen[R, A]): Gen[R, Vector[A]]
/** Set generator with size-based cardinality */
def setOf[R, A](gen: Gen[R, A]): Gen[R with Sized, Set[A]]
/** Non-empty set generator */
def setOf1[R, A](gen: Gen[R, A]): Gen[R with Sized, Set[A]]
/** Set generator with fixed size */
def setOfN[R, A](n: Int)(gen: Gen[R, A]): Gen[R, Set[A]]
/** Map generator with size-based cardinality */
def mapOf[R, A, B](keyGen: Gen[R, A], valueGen: Gen[R, B]): Gen[R with Sized, Map[A, B]]
/** Non-empty map generator */
def mapOf1[R, A, B](keyGen: Gen[R, A], valueGen: Gen[R, B]): Gen[R with Sized, Map[A, B]]
/** Map generator with fixed size */
def mapOfN[R, A, B](n: Int)(keyGen: Gen[R, A], valueGen: Gen[R, B]): Gen[R, Map[A, B]]Generators for selecting from alternatives and creating optional values.
/** Generator that chooses from multiple generators */
def oneOf[R, A](gen: Gen[R, A], gens: Gen[R, A]*): Gen[R, A]
/** Generator that chooses from values */
def elements[A](a: A, as: A*): Gen[Any, A]
/** Generator that chooses from collection */
def fromIterable[A](as: Iterable[A]): Gen[Any, A]
/** Weighted choice generator */
def weighted[R, A](gs: (Gen[R, A], Double)*): Gen[R, A]
/** None generator */
val none: Gen[Any, Option[Nothing]]
/** Some generator from another generator */
def some[R, A](gen: Gen[R, A]): Gen[R, Option[A]]
/** Optional generator (Some or None) */
def option[R, A](gen: Gen[R, A]): Gen[R, Option[A]]Generators for Either values.
/** Either generator from left and right generators */
def either[R, A, B](left: Gen[R, A], right: Gen[R, B]): Gen[R, Either[A, B]]
/** Left generator */
def left[R, A](gen: Gen[R, A]): Gen[R, Either[A, Nothing]]
/** Right generator */
def right[R, A](gen: Gen[R, A]): Gen[R, Either[Nothing, A]]Represents generated samples with shrinking capabilities.
/**
* A generated sample that can be shrunk for failure minimization
* @tparam R - Environment requirements
* @tparam A - Type of sample value
*/
case class Sample[-R, +A](value: A, shrink: ZStream[R, Nothing, Sample[R, A]]) {
/** Transform sample value */
def map[B](f: A => B): Sample[R, B]
/** Monadic composition */
def flatMap[R1 <: R, B](f: A => Sample[R1, B]): Sample[R1, B]
/** Filter sample */
def filter(f: A => Boolean): Sample[R, A]
/** Transform with effect */
def foreach[R1 <: R, B](f: A => ZIO[R1, Nothing, B]): ZIO[R1, Nothing, Sample[R1, B]]
/** Search for minimal failing sample */
def shrinkSearch(f: A => Boolean): ZStream[R, Nothing, A]
}import zio.test._
import zio.test.Gen._
// Basic property test
test("string length property") {
check(string) { s =>
assert(s.reverse.reverse)(equalTo(s))
}
}
// Multiple generators
test("addition property") {
check(int, int) { (a, b) =>
assert(a + b)(equalTo(b + a))
}
}
// Custom generator
val evenInt = int.filter(_ % 2 == 0)
test("even numbers") {
check(evenInt) { n =>
assert(n % 2)(equalTo(0))
}
}
// Collection properties
test("list concatenation") {
check(listOf(int), listOf(int)) { (xs, ys) =>
assert((xs ++ ys).length)(equalTo(xs.length + ys.length))
}
}
// Effectful property test
testM("async property") {
checkM(int) { n =>
for {
result <- someAsyncOperation(n)
} yield assert(result)(isGreaterThan(n))
}
}
// Exhaustive testing with finite generator
test("small numbers exhaustive") {
checkAll(int(1, 10)) { n =>
assert(n * n)(isGreaterThan(0))
}
}
// Fixed number of samples
test("large sample test") {
checkN(1000)(double) { d =>
assert(d + 0.0)(equalTo(d))
}
}