CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-scalacheck--scalacheck-2-12

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.

Pending
Overview
Eval results
Files

arbitrary.mddocs/

Automatic Value Generation

The ScalaCheck Arbitrary framework provides type-directed generator instances for automatic property parameterization. It eliminates the need to explicitly specify generators for common types and enables seamless integration with the property testing system.

Capabilities

Core Arbitrary Class

The fundamental type class that wraps generators and provides implicit resolution.

sealed abstract class Arbitrary[T] {
  def arbitrary: Gen[T]
}

object Arbitrary {
  def apply[T](g: => Gen[T]): Arbitrary[T]
  def arbitrary[T](implicit a: Arbitrary[T]): Gen[T]
}

Usage Examples:

// Create custom Arbitrary instance
case class Person(name: String, age: Int)
implicit val arbPerson: Arbitrary[Person] = Arbitrary {
  for {
    name <- Gen.alphaStr.suchThat(_.nonEmpty)
    age <- Gen.choose(0, 120)
  } yield Person(name, age)
}

// Use implicit generator
val personProperty = forAll { (p: Person) =>
  p.age >= 0 && p.name.nonEmpty
}

// Get generator explicitly
val personGen: Gen[Person] = Arbitrary.arbitrary[Person]

Primitive Type Instances

Implicit generators for basic Scala and Java primitive types.

implicit val arbBool: Arbitrary[Boolean]
implicit val arbInt: Arbitrary[Int]  
implicit val arbLong: Arbitrary[Long]
implicit val arbFloat: Arbitrary[Float]
implicit val arbDouble: Arbitrary[Double]
implicit val arbChar: Arbitrary[Char]
implicit val arbByte: Arbitrary[Byte]
implicit val arbShort: Arbitrary[Short]
implicit val arbUnit: Arbitrary[Unit]
implicit val arbAnyVal: Arbitrary[AnyVal]

Usage Examples:

// These work automatically due to implicit instances
val intProp = forAll { (x: Int) => x + 0 == x }
val boolProp = forAll { (b: Boolean) => b || !b }
val charProp = forAll { (c: Char) => c.toString.length == 1 }
val floatProp = forAll { (f: Float) => !f.isNaN ==> (f + 0.0f == f) }

String and Text Types

Generators for various string and symbolic text representations.

implicit val arbString: Arbitrary[String]
implicit val arbSymbol: Arbitrary[Symbol]

Usage Examples:

val stringProp = forAll { (s: String) =>
  s.reverse.reverse == s
}

val symbolProp = forAll { (sym: Symbol) =>
  Symbol(sym.name) == sym
}

Numeric Types

Arbitrary instances for big numeric types and Java numeric wrappers.

implicit val arbBigInt: Arbitrary[BigInt]
implicit val arbBigDecimal: Arbitrary[BigDecimal]
implicit val arbNumber: Arbitrary[Number]

Usage Examples:

val bigIntProp = forAll { (x: BigInt, y: BigInt) =>
  x + y == y + x
}

val bigDecimalProp = forAll { (x: BigDecimal) =>
  x * BigDecimal(1) == x
}

Date and Time Types

Generators for temporal types including legacy Java date/time and Scala durations.

implicit val arbDate: Arbitrary[java.util.Date]
implicit val arbCalendar: Arbitrary[java.util.Calendar]
implicit val arbFiniteDuration: Arbitrary[FiniteDuration]
implicit val arbDuration: Arbitrary[Duration]

Usage Examples:

val dateProp = forAll { (d: java.util.Date) =>
  new java.util.Date(d.getTime) == d
}

val durationProp = forAll { (d1: FiniteDuration, d2: FiniteDuration) =>
  (d1 + d2) >= d1 && (d1 + d2) >= d2
}

Exception Types

Generators for exception and error hierarchies.

implicit val arbThrowable: Arbitrary[Throwable]
implicit val arbException: Arbitrary[Exception]
implicit val arbError: Arbitrary[Error]

Usage Examples:

val exceptionProp = forAll { (e: Exception) =>
  e.getMessage != null || e.getMessage == null // Either is valid
}

Miscellaneous Types

Generators for UUIDs, bit sets, and other utility types.

implicit val arbUuid: Arbitrary[java.util.UUID]
implicit val arbBitSet: Arbitrary[collection.BitSet]

Usage Examples:

val uuidProp = forAll { (u: java.util.UUID) =>
  java.util.UUID.fromString(u.toString) == u
}

val bitSetProp = forAll { (bs: collection.BitSet) =>
  bs union bs == bs
}

Higher-Order Type Instances

Automatic generator derivation for generic types and type constructors.

implicit def arbOption[T](implicit a: Arbitrary[T]): Arbitrary[Option[T]]
implicit def arbEither[T, U](implicit at: Arbitrary[T], au: Arbitrary[U]): Arbitrary[Either[T, U]]
implicit def arbTry[T](implicit a: Arbitrary[T]): Arbitrary[Try[T]]
implicit def arbFuture[T](implicit a: Arbitrary[T]): Arbitrary[Future[T]]

Usage Examples:

val optionProp = forAll { (opt: Option[String]) =>
  opt.map(_.length).getOrElse(0) >= 0
}

val eitherProp = forAll { (e: Either[String, Int]) =>
  e.isLeft || e.isRight
}

val tryProp = forAll { (t: Try[Int]) =>
  t.isSuccess || t.isFailure
}

Collection Type Instances

Automatic generator derivation for various collection types.

implicit def arbContainer[C[_], T](
  implicit a: Arbitrary[T], 
  b: Buildable[T, C[T]]
): Arbitrary[C[T]]

implicit def arbContainer2[C[_, _], T, U](
  implicit at: Arbitrary[T], 
  au: Arbitrary[U], 
  b: Buildable[(T, U), C[T, U]]
): Arbitrary[C[T, U]]

Usage Examples:

val listProp = forAll { (l: List[Int]) =>
  l.reverse.reverse == l
}

val vectorProp = forAll { (v: Vector[String]) =>
  v.length >= 0
}

val mapProp = forAll { (m: Map[String, Int]) =>
  m.keys.size <= m.size
}

val setProp = forAll { (s: Set[Double]) =>
  s union s == s
}

Function Type Instances

Generators for function types using co-generators.

implicit def arbFunction0[T](implicit a: Arbitrary[T]): Arbitrary[() => T]
implicit def arbPartialFunction[A, B](
  implicit aa: Arbitrary[A], 
  ca: Cogen[A], 
  ab: Arbitrary[B]
): Arbitrary[PartialFunction[A, B]]

Usage Examples:

val function0Prop = forAll { (f: () => Int) =>
  f() == f() // Should be deterministic
}

val pfProp = forAll { (pf: PartialFunction[Int, String], x: Int) =>
  pf.isDefinedAt(x) ==> (pf(x) != null)
}

Java Enum Support

Automatic generator derivation for Java enumeration types.

implicit def arbEnum[A <: java.lang.Enum[A]](
  implicit tag: reflect.ClassTag[A]
): Arbitrary[A]

Usage Examples:

// For Java enum types
enum Color { RED, GREEN, BLUE }

val colorProp = forAll { (c: Color) =>
  c == Color.RED || c == Color.GREEN || c == Color.BLUE
}

Test Infrastructure Types

Generators for ScalaCheck's own types, useful for meta-testing.

implicit val arbProp: Arbitrary[Prop]
implicit val arbTestParameters: Arbitrary[Test.Parameters]
implicit val arbGenParams: Arbitrary[Gen.Parameters]
implicit def arbGen[T](implicit a: Arbitrary[T]): Arbitrary[Gen[T]]

Usage Examples:

val propProp = forAll { (p: Prop) =>
  // Meta-property: properties should be checkable
  try { p.check(); true } catch { case _: Exception => true }
}

val genProp = forAll { (g: Gen[Int]) =>
  g.sample.isDefined || g.sample.isEmpty
}

Custom Arbitrary Instances

Simple Custom Types

case class Email(value: String)

implicit val arbEmail: Arbitrary[Email] = Arbitrary {
  for {
    user <- Gen.alphaNumStr.suchThat(_.nonEmpty)
    domain <- Gen.alphaNumStr.suchThat(_.nonEmpty)
    tld <- Gen.oneOf("com", "org", "net", "edu")
  } yield Email(s"$user@$domain.$tld")
}

Recursive Data Structures

sealed trait Tree[+A]
case class Leaf[A](value: A) extends Tree[A]  
case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]

implicit def arbTree[A](implicit a: Arbitrary[A]): Arbitrary[Tree[A]] = Arbitrary {
  val genLeaf = a.arbitrary.map(Leaf(_))
  
  def genBranch(size: Int): Gen[Tree[A]] = 
    if (size <= 0) genLeaf
    else Gen.oneOf(
      genLeaf,
      for {
        left <- genBranch(size / 2)
        right <- genBranch(size / 2)
      } yield Branch(left, right)
    )
  
  Gen.sized(genBranch)
}

Constrained Generation

case class PositiveInt(value: Int)

implicit val arbPositiveInt: Arbitrary[PositiveInt] = Arbitrary {
  Gen.choose(1, Int.MaxValue).map(PositiveInt)
}

case class NonEmptyList[A](values: List[A])

implicit def arbNonEmptyList[A](implicit a: Arbitrary[A]): Arbitrary[NonEmptyList[A]] = Arbitrary {
  Gen.nonEmptyListOf(a.arbitrary).map(NonEmptyList(_))
}

Complex Business Objects

case class User(
  id: Long,
  username: String, 
  email: String,
  age: Int,
  roles: Set[String],
  settings: Map[String, String]
)

implicit val arbUser: Arbitrary[User] = Arbitrary {
  for {
    id <- Gen.posNum[Long]
    username <- Gen.alphaNumStr.suchThat(s => s.length >= 3 && s.length <= 20)
    email <- Arbitrary.arbitrary[Email].map(_.value)
    age <- Gen.choose(13, 120)
    roleCount <- Gen.choose(1, 5)
    roles <- Gen.listOfN(roleCount, Gen.oneOf("user", "admin", "moderator", "guest"))
                .map(_.toSet)
    settingCount <- Gen.choose(0, 10)
    settingKeys <- Gen.listOfN(settingCount, Gen.alphaStr.suchThat(_.nonEmpty))
    settingValues <- Gen.listOfN(settingCount, Gen.alphaNumStr)
    settings = settingKeys.zip(settingValues).toMap
  } yield User(id, username, email, age, roles, settings)
}

Arbitrary Resolution Patterns

Type Class Derivation Chain

// Automatic derivation works through implicit chains
val nestedProp = forAll { (data: Map[String, List[Option[Either[Int, String]]]]) =>
  // This works because:
  // 1. Arbitrary[Int] and Arbitrary[String] exist
  // 2. Arbitrary[Either[Int, String]] is derived
  // 3. Arbitrary[Option[Either[Int, String]]] is derived  
  // 4. Arbitrary[List[Option[Either[Int, String]]]] is derived
  // 5. Arbitrary[Map[String, List[Option[Either[Int, String]]]]] is derived
  data.size >= 0
}

Manual Generator Override

// Sometimes you want a specific generator for a property
val customProp = forAll(Gen.choose(1, 10)) { (smallInt: Int) =>
  // Uses explicit generator instead of Arbitrary[Int]
  smallInt >= 1 && smallInt <= 10
}

// Mix explicit and implicit generators  
val mixedProp = forAll(Gen.choose(1, 100), Arbitrary.arbitrary[String]) { (smallInt, str) =>
  smallInt.toString.length <= str.length || str.isEmpty
}

Conditional Arbitrary Instances

// Provide different generators based on type constraints
implicit def arbOrderedPair[T](implicit a: Arbitrary[T], ord: Ordering[T]): Arbitrary[(T, T)] = Arbitrary {
  for {
    x <- a.arbitrary
    y <- a.arbitrary
  } yield if (ord.lt(x, y)) (x, y) else (y, x)
}

Java Time Support

ScalaCheck provides built-in Arbitrary instances for Java 8 time types through the org.scalacheck.time package.

// Import for Java Time support
import org.scalacheck.time._

// Date and Time Types
implicit val arbDuration: Arbitrary[java.time.Duration]
implicit val arbInstant: Arbitrary[java.time.Instant]
implicit val arbLocalDate: Arbitrary[java.time.LocalDate]
implicit val arbLocalTime: Arbitrary[java.time.LocalTime] 
implicit val arbLocalDateTime: Arbitrary[java.time.LocalDateTime]
implicit val arbOffsetTime: Arbitrary[java.time.OffsetTime]
implicit val arbOffsetDateTime: Arbitrary[java.time.OffsetDateTime]
implicit val arbZonedDateTime: Arbitrary[java.time.ZonedDateTime]

// Period and Year Types
implicit val arbPeriod: Arbitrary[java.time.Period]
implicit val arbYear: Arbitrary[java.time.Year]
implicit val arbYearMonth: Arbitrary[java.time.YearMonth]
implicit val arbMonthDay: Arbitrary[java.time.MonthDay]

// Zone Types
implicit val arbZoneOffset: Arbitrary[java.time.ZoneOffset]
implicit val arbZoneId: Arbitrary[java.time.ZoneId]

Usage Examples:

import org.scalacheck.time._
import java.time._

val timestampProp = forAll { (instant: Instant) =>
  instant.getEpochSecond >= Instant.MIN.getEpochSecond &&
  instant.getEpochSecond <= Instant.MAX.getEpochSecond
}

val dateProp = forAll { (date: LocalDate) =>
  date.isAfter(LocalDate.MIN) || date.isEqual(LocalDate.MIN)
}

val durationProp = forAll { (d1: Duration, d2: Duration) =>
  d1.plus(d2).minus(d1) == d2
}

val zonedTimeProp = forAll { (zdt: ZonedDateTime) =>
  zdt.toInstant.atZone(zdt.getZone) == zdt
}

Install with Tessl CLI

npx tessl i tessl/maven-org-scalacheck--scalacheck-2-12

docs

arbitrary.md

cogen.md

generators.md

index.md

properties.md

property-collections.md

shrinking.md

stateful-testing.md

test-execution.md

tile.json