A comprehensive property-based testing library for Scala and Java applications that enables developers to specify program properties as testable assertions and automatically generates test cases to verify these properties.
—
The ScalaCheck generator framework provides comprehensive data generation capabilities for property-based testing. Generators create test values with configurable size parameters, deterministic seeds, and powerful combinators for building complex test data.
The fundamental generator abstraction with transformation, filtering, and sampling capabilities.
abstract class Gen[+T] {
def map[U](f: T => U): Gen[U]
def flatMap[U](f: T => Gen[U]): Gen[U]
def filter(p: T => Boolean): Gen[T]
def filterNot(p: T => Boolean): Gen[T]
def suchThat(f: T => Boolean): Gen[T]
def retryUntil(p: T => Boolean, maxTries: Int = 10): Gen[T]
def sample: Option[T]
def pureApply(p: Gen.Parameters, seed: Seed, retries: Int = 100): T
def label(l: String): Gen[T]
def :|(l: String): Gen[T]
def |:(l: String): Gen[T]
}Usage Example:
val evenInts = Gen.choose(1, 100).filter(_ % 2 == 0)
val positiveStrings = Gen.alphaStr.suchThat(_.nonEmpty)
val taggedGen = Gen.choose(1, 10).label("small integers")Fundamental generators for constant values, ranges, and selections.
object Gen {
def const[T](x: T): Gen[T]
def fail[T]: Gen[T]
def choose[T](min: T, max: T)(implicit num: Choose[T]): Gen[T]
def oneOf[T](xs: Iterable[T]): Gen[T]
def oneOf[T](t0: T, t1: T, tn: T*): Gen[T]
def frequency[T](gs: (Int, Gen[T])*): Gen[T]
def prob(chance: Double): Gen[Boolean]
}Usage Examples:
val alwaysTrue = Gen.const(true)
val dice = Gen.choose(1, 6)
val coin = Gen.oneOf(true, false)
val weightedCoin = Gen.frequency(3 -> true, 1 -> false) // 75% true
val biasedCoin = Gen.prob(0.7) // 70% trueGenerators for optional values and Either types.
def option[T](g: Gen[T]): Gen[Option[T]]
def some[T](g: Gen[T]): Gen[Option[T]]
def either[T, U](gt: Gen[T], gu: Gen[U]): Gen[Either[T, U]]Usage Examples:
val maybeInt = Gen.option(Gen.choose(1, 10))
val someInt = Gen.some(Gen.choose(1, 10)) // never generates None
val stringOrInt = Gen.either(Gen.alphaStr, Gen.choose(1, 100))Comprehensive collection generation with size control and various collection types.
def listOf[T](g: Gen[T]): Gen[List[T]]
def listOfN[T](n: Int, g: Gen[T]): Gen[List[T]]
def nonEmptyListOf[T](g: Gen[T]): Gen[List[T]]
def containerOf[C[_], T](g: Gen[T])(implicit b: Buildable[T, C[T]]): Gen[C[T]]
def containerOfN[C[_], T](n: Int, g: Gen[T])(implicit b: Buildable[T, C[T]]): Gen[C[T]]
def nonEmptyContainerOf[C[_], T](g: Gen[T])(implicit b: Buildable[T, C[T]]): Gen[C[T]]
def mapOf[T, U](g: Gen[(T, U)]): Gen[Map[T, U]]
def mapOfN[T, U](n: Int, g: Gen[(T, U)]): Gen[Map[T, U]]
def nonEmptyMap[T, U](g: Gen[(T, U)]): Gen[Map[T, U]]Usage Examples:
val intLists = Gen.listOf(Gen.choose(1, 10))
val exactly5Ints = Gen.listOfN(5, Gen.choose(1, 100))
val nonEmptyStrings = Gen.nonEmptyListOf(Gen.alphaChar.map(_.toString))
val intToStringMap = Gen.mapOf(Gen.zip(Gen.choose(1, 10), Gen.alphaStr))
val vectors = Gen.containerOf[Vector, Int](Gen.choose(1, 100))Generators for selecting subsets and elements from existing collections.
def pick[T](n: Int, l: Iterable[T]): Gen[Seq[T]]
def someOf[T](l: Iterable[T]): Gen[Seq[T]]
def atLeastOne[T](l: Iterable[T]): Gen[Seq[T]]Usage Examples:
val colors = List("red", "green", "blue", "yellow", "purple")
val threeColors = Gen.pick(3, colors)
val someColors = Gen.someOf(colors)
val atLeastOneColor = Gen.atLeastOne(colors)Specialized generators for characters and strings with various character sets.
val numChar: Gen[Char]
val alphaUpperChar: Gen[Char]
val alphaLowerChar: Gen[Char]
val alphaChar: Gen[Char]
val alphaNumChar: Gen[Char]
val asciiChar: Gen[Char]
val asciiPrintableChar: Gen[Char]
val hexChar: Gen[Char]
def stringOf(gc: Gen[Char]): Gen[String]
def stringOfN(n: Int, gc: Gen[Char]): Gen[String]
def nonEmptyStringOf(gc: Gen[Char]): Gen[String]
val identifier: Gen[String]
val numStr: Gen[String]
val alphaStr: Gen[String]
val alphaNumStr: Gen[String]
val asciiStr: Gen[String]
val hexStr: Gen[String]Usage Examples:
val passwords = Gen.stringOfN(8, Gen.alphaNumChar)
val identifiers = Gen.identifier
val hexStrings = Gen.stringOf(Gen.hexChar)
val nonEmptyNames = Gen.nonEmptyStringOf(Gen.alphaChar)Generators for various numeric types with special values and distributions.
val long: Gen[Long]
val double: Gen[Double] // [0, 1)
def posNum[T](implicit num: Numeric[T], c: Choose[T]): Gen[T]
def negNum[T](implicit num: Numeric[T], c: Choose[T]): Gen[T]
def chooseNum[T](minT: T, maxT: T, specials: T*)(implicit num: Numeric[T], c: Choose[T]): Gen[T]
// Distribution generators
def gaussian(mean: Double, stdDev: Double): Gen[Double]
def exponential(rate: Double): Gen[Double]
def geometric(mean: Double): Gen[Int]
def poisson(rate: Double): Gen[Int]
def binomial(test: Gen[Boolean], trials: Int): Gen[Int]Usage Examples:
val positiveInts = Gen.posNum[Int]
val negativeDoubles = Gen.negNum[Double]
val specialInts = Gen.chooseNum(1, 100, 42, 13, 7) // includes special values
val normalDistrib = Gen.gaussian(0.0, 1.0)
val coinFlips = Gen.binomial(Gen.prob(0.5), 10)Generators for UUIDs, dates, and durations.
val uuid: Gen[UUID]
val calendar: Gen[Calendar]
val finiteDuration: Gen[FiniteDuration]
val duration: Gen[Duration] // includes infinite durationsUsage Examples:
val ids = Gen.uuid
val timestamps = Gen.calendar
val timeouts = Gen.finiteDuration
val allDurations = Gen.duration // may include Duration.InfPowerful combinators for complex generator composition and control flow.
def sequence[C[_], T](gs: Traversable[Gen[T]])(implicit b: Buildable[T, C[T]]): Gen[C[T]]
def tailRecM[A, B](a0: A)(fn: A => Gen[Either[A, B]]): Gen[B]
def lzy[T](g: => Gen[T]): Gen[T]
def delay[T](g: => Gen[T]): Gen[T]
def recursive[A](fn: Gen[A] => Gen[A]): Gen[A]
def parameterized[T](f: Gen.Parameters => Gen[T]): Gen[T]
def sized[T](f: Int => Gen[T]): Gen[T]
val size: Gen[Int]
def resize[T](s: Int, g: Gen[T]): Gen[T]Usage Examples:
// Generate lists of generators into generator of lists
val genList = List(Gen.choose(1, 10), Gen.choose(20, 30), Gen.choose(40, 50))
val combined = Gen.sequence(genList)
// Size-dependent generation
val sizedLists = Gen.sized(n => Gen.listOfN(n, Gen.choose(1, 100)))
// Recursive data structures
case class Tree(value: Int, children: List[Tree])
val treeGen = Gen.recursive[Tree] { recurse =>
for {
value <- Gen.choose(1, 100)
size <- Gen.choose(0, 3)
children <- Gen.listOfN(size, recurse)
} yield Tree(value, children)
}
// Parameter access
val customGen = Gen.parameterized { params =>
Gen.listOfN(params.size, Gen.alphaChar)
}Combining multiple generators into tuples.
def zip[T, U](gt: Gen[T], gu: Gen[U]): Gen[(T, U)]
def zip[A, B, C](ga: Gen[A], gb: Gen[B], gc: Gen[C]): Gen[(A, B, C)]
// ... up to 9-tuplesUsage Examples:
val coordinates = Gen.zip(Gen.choose(-100, 100), Gen.choose(-100, 100))
val person = Gen.zip(Gen.alphaStr, Gen.choose(18, 80), Gen.oneOf("M", "F"))sealed abstract class Gen.Parameters {
val size: Int
val initialSeed: Option[Seed]
val useLegacyShrinking: Boolean
def withSize(size: Int): Gen.Parameters
def withInitialSeed(seed: Seed): Gen.Parameters
def withLegacyShrinking(b: Boolean): Gen.Parameters
}
object Gen.Parameters {
val default: Gen.Parameters
}trait Choose[T] {
def choose(min: T, max: T): Gen[T]
}
// Implicit instances for numeric types
implicit val chooseInt: Choose[Int]
implicit val chooseLong: Choose[Long]
implicit val chooseDouble: Choose[Double]
implicit val chooseChar: Choose[Char]
implicit val chooseBigInt: Choose[BigInt]
implicit val chooseBigDecimal: Choose[BigDecimal]
implicit val chooseFiniteDuration: Choose[FiniteDuration]val personGen = for {
name <- Gen.alphaStr.suchThat(_.length > 2)
age <- Gen.choose(18, 80)
email <- Gen.alphaStr.map(_ + "@example.com")
} yield Person(name, age, email)val dataGen = Gen.oneOf(true, false).flatMap { isComplex =>
if (isComplex) complexDataGen else simpleDataGen
}val priorityGen = Gen.frequency(
1 -> "LOW",
3 -> "MEDIUM",
5 -> "HIGH",
1 -> "CRITICAL"
)Install with Tessl CLI
npx tessl i tessl/maven-org-scalacheck--scalacheck-2-12