Testing utilities and helpers for the Cats functional programming library with controlled type class instances for property-based testing
The Helper Types capability provides concrete data types where you can control exactly which type class instances are available. This enables precise testing of algebraic laws and type class behaviors.
All helper types extend the base N trait and have controlled type class instances:
Arbitrary[T] and Cogen[T] for ScalaCheck property testingabstract class N {
def n: Int
}
abstract class Arb[E <: N](f: Int => E) {
implicit val earb: Arbitrary[E]
implicit val ccog: Cogen[E]
}
trait Q[E] {
implicit val eeq: Eq[E]
}
abstract class Companion[E <: N](f: Int => E) extends Arb[E](f) with Q[E]For testing types that only need equality comparison.
case class Eqed(n: Int) extends N
object Eqed extends Companion(new Eqed(_))Usage example:
import cats.tests.Helpers.Eqed
import cats.kernel.laws.discipline.EqTests
checkAll("Eq[Eqed]", EqTests[Eqed].eqv)For testing partial ordering laws where not all elements are comparable.
case class POrd(n: Int) extends N
object POrd extends Arb(new POrd(_)) {
implicit object O extends PartialOrder[POrd] {
def partialCompare(x: POrd, y: POrd): Double
}
}Usage example:
import cats.tests.Helpers.POrd
import cats.kernel.laws.discipline.PartialOrderTests
checkAll("PartialOrder[POrd]", PartialOrderTests[POrd].partialOrder)For testing total ordering laws.
case class Ord(n: Int) extends N
object Ord extends Arb(new Ord(_)) {
implicit object O extends Order[Ord] {
def compare(x: Ord, y: Ord): Int
}
}Usage example:
import cats.tests.Helpers.Ord
import cats.kernel.laws.discipline.OrderTests
checkAll("Order[Ord]", OrderTests[Ord].order)For testing hash-based equality.
case class Hsh(n: Int) extends N
object Hsh extends Arb(new Hsh(_)) {
implicit object O extends Hash[Hsh] {
def hash(x: Hsh): Int
def eqv(x: Hsh, y: Hsh): Boolean
}
}For testing idempotent semigroup laws (where x combine x = x).
case class Bnd(n: Int) extends N
object Bnd extends Companion(new Bnd(_)) {
implicit object Alg extends Band[Bnd] {
def combine(x: Bnd, y: Bnd): Bnd
}
}For testing semilattice laws (idempotent and commutative semigroup).
case class SL(n: Int) extends N
object SL extends Companion(new SL(_)) {
implicit object Alg extends Semilattice[SL] {
def combine(x: SL, y: SL): SL
}
}For testing bounded semilattice laws (semilattice with identity element).
case class BSL(n: Int) extends N
object BSL extends Companion(new BSL(_)) {
implicit object Alg extends BoundedSemilattice[BSL] {
def empty: BSL
def combine(x: BSL, y: BSL): BSL
}
}For testing basic semigroup laws (associativity).
case class Semi(n: Int) extends N
object Semi extends Companion(new Semi(_)) {
implicit object Alg extends Semigroup[Semi] {
def combine(x: Semi, y: Semi): Semi
}
}For testing commutative semigroup laws.
case class CSemi(n: Int) extends N
object CSemi extends Companion(new CSemi(_)) {
implicit object Alg extends CommutativeSemigroup[CSemi] {
def combine(x: CSemi, y: CSemi): CSemi
}
}For testing monoid laws (semigroup with identity).
case class Mono(n: Int) extends N
object Mono extends Companion(new Mono(_)) {
implicit object Alg extends Monoid[Mono] {
def empty: Mono
def combine(x: Mono, y: Mono): Mono
}
}Usage example:
import cats.tests.Helpers.Mono
import cats.kernel.laws.discipline.MonoidTests
checkAll("Monoid[Mono]", MonoidTests[Mono].monoid)For testing commutative monoid laws.
case class CMono(n: Int) extends N
object CMono extends Companion(new CMono(_)) {
implicit object Alg extends CommutativeMonoid[CMono] {
def empty: CMono
def combine(x: CMono, y: CMono): CMono
}
}For testing group laws (monoid with inverse).
case class Grp(n: Int) extends N
object Grp extends Companion(new Grp(_)) {
implicit object Alg extends Group[Grp] {
def empty: Grp
def combine(x: Grp, y: Grp): Grp
def inverse(x: Grp): Grp
}
}Usage example:
import cats.tests.Helpers.Grp
import cats.kernel.laws.discipline.GroupTests
checkAll("Group[Grp]", GroupTests[Grp].group)For testing commutative group laws.
case class CGrp(n: Int) extends N
object CGrp extends Companion(new CGrp(_)) {
implicit object Alg extends CommutativeGroup[CGrp] {
def empty: CGrp
def combine(x: CGrp, y: CGrp): CGrp
def inverse(x: CGrp): CGrp
}
}The typical pattern for using helper types in property-based testing:
import cats.tests.Helpers._
import cats.kernel.laws.discipline._
import org.scalatest.funsuite.AnyFunSuite
import org.scalatestplus.scalacheck.Checkers
class AlgebraicLawTests extends AnyFunSuite with Checkers {
test("semigroup laws") {
checkAll("Semigroup[Semi]", SemigroupTests[Semi].semigroup)
}
test("monoid laws") {
checkAll("Monoid[Mono]", MonoidTests[Mono].monoid)
}
test("group laws") {
checkAll("Group[Grp]", GroupTests[Grp].group)
}
}Each helper type provides exactly the type class instances needed for testing specific algebraic laws, ensuring that tests focus on the intended algebraic structure without interference from additional instances.
Install with Tessl CLI
npx tessl i tessl/maven-org-typelevel--cats-testkit