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 property framework enables expressing testable assertions about program behavior. Properties can be combined with logical operators, parameterized with generators, and executed with comprehensive reporting capabilities.
The fundamental property abstraction with logical combinators and execution methods.
abstract class Prop {
def apply(prms: Gen.Parameters): Prop.Result
def &&(p: => Prop): Prop
def ||(p: => Prop): Prop
def ++(p: => Prop): Prop
def ==>(p: => Prop): Prop
def ==(p: => Prop): Prop
def check(): Unit
def check(prms: Test.Parameters): Unit
def check(paramFun: Test.Parameters => Test.Parameters): Unit
def label(l: String): Prop
def :|(l: String): Prop
def |:(l: String): Prop
def useSeed(seed: Seed): Prop
def viewSeed(name: String): Prop
}Usage Examples:
val prop1 = Prop.forAll { (x: Int) => x + 0 == x }
val prop2 = Prop.forAll { (x: Int) => x * 1 == x }
val combinedProp = prop1 && prop2
val conditionalProp = (prop1 ==> prop2).label("identity laws")
combinedProp.check()
conditionalProp.check(_.withMinSuccessfulTests(1000))Factory methods for creating properties from various sources.
object Prop {
def apply(f: Gen.Parameters => Prop.Result): Prop
def apply(r: Prop.Result): Prop
def apply(b: Boolean): Prop
val undecided: Prop
val falsified: Prop
val proved: Prop
val passed: Prop
val exception: Prop
}Usage Examples:
val trueProp = Prop(true)
val falseProp = Prop(false)
val customProp = Prop { params =>
if (params.size > 10) Prop.Result(Prop.Passed)
else Prop.Result(Prop.Undecided)
}Properties that must hold for all generated inputs, with support for explicit and implicit generators.
// With explicit generators (1-8 parameters)
def forAll[T1, P](g1: Gen[T1])(f: T1 => P)(implicit pp: P => Prop): Prop
def forAll[T1, T2, P](g1: Gen[T1], g2: Gen[T2])(f: (T1, T2) => P)(implicit pp: P => Prop): Prop
def forAll[T1, T2, T3, P](g1: Gen[T1], g2: Gen[T2], g3: Gen[T3])(f: (T1, T2, T3) => P)(implicit pp: P => Prop): Prop
// ... up to 8 parameters
// With implicit generators (1-8 parameters)
def forAll[A1, P](f: A1 => P)(implicit a1: Arbitrary[A1], pp: P => Prop): Prop
def forAll[A1, A2, P](f: (A1, A2) => P)(implicit a1: Arbitrary[A1], a2: Arbitrary[A2], pp: P => Prop): Prop
def forAll[A1, A2, A3, P](f: (A1, A2, A3) => P)(implicit a1: Arbitrary[A1], a2: Arbitrary[A2], a3: Arbitrary[A3], pp: P => Prop): Prop
// ... up to 8 parameters
// No-shrink variants
def forAllNoShrink[T1, P](g1: Gen[T1])(f: T1 => P)(implicit pp: P => Prop): Prop
// ... similar variants up to 8 parametersUsage Examples:
// Implicit generators
val listReverseProp = forAll { (l: List[Int]) =>
l.reverse.reverse == l
}
// Explicit generators
val smallIntProp = forAll(Gen.choose(1, 100)) { n =>
n > 0 && n <= 100
}
// Multiple parameters
val additionProp = forAll(Gen.choose(1, 100), Gen.choose(1, 100)) { (a, b) =>
a + b > a && a + b > b
}
// No shrinking for performance-sensitive tests
val noShrinkProp = forAllNoShrink(complexGen) { data =>
expensiveProperty(data)
}Properties that must hold for at least one generated input.
def exists[A, P](f: A => P)(implicit a: Arbitrary[A], pp: P => Prop): Prop
def exists[A, P](g: Gen[A])(f: A => P)(implicit pp: P => Prop): PropUsage Examples:
val existsPrime = exists { (n: Int) =>
n > 1 && isPrime(n)
}
val existsEven = exists(Gen.choose(1, 100)) { n =>
n % 2 == 0
}Specialized properties for testing equality relationships.
def ?=[T](x: T, y: T): Prop
def =?[T](x: T, y: T): PropUsage Examples:
val equalityProp = forAll { (s: String) =>
s.length ?= s.toList.length
}
val reverseEqualityProp = forAll { (l: List[Int]) =>
l.size =? l.reverse.size
}Combining and transforming properties with logical operators.
def all(ps: Prop*): Prop
def atLeastOne(ps: Prop*): Prop
def collect[T](t: T)(prop: Prop): Prop
def classify(c: => Boolean, ifTrue: Any)(prop: Prop): Prop
def classify(c: => Boolean, ifTrue: Any, ifFalse: Any)(prop: Prop): PropUsage Examples:
val allProps = all(
forAll((x: Int) => x + 0 == x),
forAll((x: Int) => x * 1 == x),
forAll((x: Int) => x - x == 0)
)
val collectingProp = forAll { (l: List[Int]) =>
collect(l.length) {
l.reverse.reverse == l
}
}
val classifyingProp = forAll { (l: List[Int]) =>
classify(l.isEmpty, "empty list", "non-empty list") {
l.reverse.length == l.length
}
}Testing exception behavior and protecting against unsafe operations.
def secure[P](p: => P)(implicit pp: P => Prop): Prop
def throws[T <: Throwable](c: Class[T])(x: => Any): Boolean
def within(maximumMs: Long)(wrappedProp: => Prop): PropUsage Examples:
val divisionProp = forAll { (a: Int, b: Int) =>
secure {
if (b != 0) a / b == a / b else throws(classOf[ArithmeticException])(a / b)
}
}
val timeoutProp = forAll { (data: LargeDataSet) =>
within(5000) { // 5 seconds max
processData(data).size >= 0
}
}
val exceptionProp = Prop(throws(classOf[IllegalArgumentException])(processInvalidInput()))Control over property evaluation timing and recursion.
def delay(p: => Prop): Prop
def lzy(p: => Prop): Prop
def protect(p: => Prop): Prop
def sizedProp(f: Int => Prop): PropUsage Examples:
val lazyProp = lzy {
// Expensive property that should be evaluated lazily
forAll(expensiveGen)(expensiveTest)
}
val sizedProperty = sizedProp { size =>
forAll(Gen.listOfN(size, Gen.choose(1, 100))) { l =>
l.length == size
}
}
val protectedProp = protect {
// May throw during property construction
forAll(riskyGen)(riskyTest)
}Implication and conditional property evaluation.
def imply[T](x: T, f: PartialFunction[T, Prop]): Prop
def iff[T](x: T, f: PartialFunction[T, Prop]): PropUsage Examples:
val conditionalProp = forAll { (l: List[Int]) =>
imply(l) {
case list if list.nonEmpty => list.head == list.head
}
}
val biconditionalProp = forAll { (n: Int) =>
iff(n) {
case num if num > 0 => num * num > 0
}
}Automatic conversions and operator extensions for enhanced syntax.
implicit def propBoolean(b: Boolean): Prop
implicit def AnyOperators[T](x: => T): ExtendedAny[T]
class ExtendedAny[T](x: => T) {
def imply(f: PartialFunction[T, Prop]): Prop
def iff(f: PartialFunction[T, Prop]): Prop
def ?=(y: T): Prop
def =?(y: T): Prop
}
class ExtendedBoolean(b: => Boolean) {
def ==>(p: => Prop): Prop
def :|(l: String): Prop
}Usage Examples:
// Boolean to Prop conversion
val boolProp: Prop = (5 > 3)
// Extended operators
val impliedProp = (someCondition: Boolean) ==> forAll { (x: Int) => x >= 0 }
val equalityProp = forAll { (a: Int, b: Int) =>
(a + b) ?= (b + a)
}
// Labels with operators
val labeledProp = (x > y) :| s"Expected $x > $y"case class Prop.Result(
status: Prop.Status,
args: List[Prop.Arg[Any]],
collected: Set[Any],
labels: Set[String]
) {
def success: Boolean
def failure: Boolean
def proved: Boolean
}
sealed trait Prop.Status
case object Prop.Proof extends Prop.Status
case object Prop.True extends Prop.Status
case object Prop.False extends Prop.Status
case object Prop.Undecided extends Prop.Status
case class Prop.Exception(e: Throwable) extends Prop.Status
case class Prop.Arg[+T](
label: String,
arg: T,
shrinks: Int,
origArg: T,
prettyArg: Pretty,
prettyOrigArg: Pretty
)val mathProps =
forAll((a: Int) => a + 0 == a) &&
forAll((a: Int) => a * 1 == a) &&
forAll((a: Int) => a - a == 0)
val anyProps =
forAll((x: Int) => x > 0) ||
forAll((x: Int) => x == 0) ||
forAll((x: Int) => x < 0)val sortedListProp = forAll { (l: List[Int]) =>
val sorted = l.sorted
(l.nonEmpty ==> (sorted.head <= sorted.last)) &&
(sorted.length ?= l.length)
}val statisticalProp = forAll { (l: List[Int]) =>
classify(l.isEmpty, "empty") {
classify(l.length < 10, "small", "large") {
collect(l.length) {
l.reverse.reverse == l
}
}
}
}object CollectionProperties extends Properties("Collections") {
val listProps = all(
forAll((l: List[Int]) => l.reverse.reverse == l),
forAll((l: List[Int]) => l ++ Nil == l),
forAll((l: List[Int]) => Nil ++ l == l)
)
val setProps = all(
forAll((s: Set[Int]) => s union s == s),
forAll((s: Set[Int]) => s intersect s == s)
)
property("lists") = listProps
property("sets") = setProps
}Install with Tessl CLI
npx tessl i tessl/maven-org-scalacheck--scalacheck-2-12