property based testing with scalacheck

Post on 04-Jun-2015

477 Views

Category:

Presentations & Public Speaking

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

From my talk at the conference Scala.IO at Paris in October 2014

TRANSCRIPT

val propMax = Prop.forAll { (x: Int, y: Int) => val z = math.max(x, y) (z == x || z == y) && (z >= x && z >= y)}

propMax.check + OK, passed 100 tests.

val propMax = Prop.forAll { (x: Int, y: Int) => val z = math.max(x, y) (z == x || z == y) && (z >= x && z >= y)}

propMax.check + OK, passed 100 tests.

val propMax = Prop.forAll { (x: Int, y: Int) => val z = math.max(x, y) (z == x || z == y) && (z >= x && z >= y)}

propMax.check + OK, passed 100 tests.

val propMax = Prop.forAll { (x: Int, y: Int) => val z = math.max(x, y) (z == x || z == y) && (z >= x && z >= y)}

propMax.check + OK, passed 100 tests.

val propMax = Prop.forAll { (x: Int, y: Int) => val z = math.max(x, y) (z == x || z == y) && (z >= x && z >= y)}

propMax.check + OK, passed 100 tests.

class Gen[+T] { def apply(prms: Gen.Params): Option[T] }

Gen.choose(0, 100).sample res0: Option[Int] = Some(89) Prop.forAll(Gen.choose(0, 100)) ( _ <= 100 ).check + OK, passed 100 tests.

scala> Gen. alphaChar fail nonEmptyContainerOf sequence alphaLowerChar freqTuple nonEmptyListOf size alphaNumChar frequency nonEmptyMap sized alphaStr identifier numChar someOf alphaUpperChar isInstanceOf numStr toString asInstanceOf listOf oneOf uuid choose listOf1 option value chooseNum listOfN parameterized wrap const lzy pick zip containerOf mapOf posNum containerOf1 mapOfN resize containerOfN negNum resultOf

Custom Generators

val personGenerator = for { name <- Gen.alphaStr age <- Gen.choose(0, 150) } yield Person(name, age)Prop.forAll(personGenerator :| "Person") { p => p.toString ?= s"Person(${p.name},${p.age})"} case class Person(name: String, age: Int)

for { s <- Arbitrary.arbitrary[String] index <- Gen.choose(0, s.length)} yield (s, index)

val even = Gen.posNum[Int].suchThat(_ % 2 == 0) val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) val number = Gen.frequency((2, even), (4, odd), (1, 0))Prop.forAll(number) { i => val label = if (i == 0) "zero" else if (i % 2 == 0) "even" else "odd" Prop.collect(label)(true) }.check(1000)

+ OK, passed 1000 tests. > Collected test data: 57% odd 29% even 14% zero

val even = Gen.posNum[Int].suchThat(_ % 2 == 0) val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) val number = Gen.frequency((2, even), (4, odd), (1, 0))Prop.forAll(number) { i => val label = if (i == 0) "zero" else if (i % 2 == 0) "even" else "odd" Prop.collect(label)(true) }.check(1000)

+ OK, passed 1000 tests. > Collected test data: 57% odd 29% even 14% zero

val even = Gen.posNum[Int].suchThat(_ % 2 == 0) val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) val number = Gen.frequency((2, even), (4, odd), (1, 0))Prop.forAll(number) { i => val label = if (i == 0) "zero" else if (i % 2 == 0) "even" else "odd" Prop.collect(label)(true) }.check(1000)

+ OK, passed 1000 tests. > Collected test data: 57% odd 29% even 14% zero

sealed trait Coincase object Head extends Coincase object Tail extends Coin

val coin = Gen.oneOf(Head, Tail)

val coins = Gen.listOf(coin)

val threeCoins = Gen.listOfN(3, coin)

scala> coins.sample res1: Option[List[Coin]] = Some(List(Head, Tail, Head, Tail, …))

scala> threeCoins.sample res2: Option[List[Coin]] = Some(List(Head, Head, Tail))

Coin

Coin

Int,String… Int, String…

Prop.forAll { xs: List[Int] => xs.reverse.reverse == xs }

Prop.forAll { xs: List[Int] => xs.reverse.reverse == xs }

Prop.forAll { xs: List[Int] => xs.reverse.reverse == xs }

import org.scalacheck.Prop.BooleanOperators

Prop.forAll { x: Int => (x >= 0) ==> { val root = math.sqrt(x) math.round(root * root) == x }}

val p = Prop.forAll { n: Int => (n >= 0 && n % 2 == 0 && n % 3 == 0) ==> n >= 0 } scala> p.check ! Gave up after only 52 passed tests. 262 tests were discarded.

val p = Prop.forAll { n: Int => (n >= 0 && n % 2 == 0 && n % 3 == 0) ==> n >= 0 } scala> p.check ! Gave up after only 52 passed tests. 262 tests were discarded.

val genNum = Gen.choose(0, Int.MaxValue) .retryUntil(_ % 2 == 0) .retryUntil(_ % 3 == 0) val p = Prop.forAll(genNum) { x: Int => x >= 0 } scala> p.check + OK, passed 100 tests.

val p = Prop.forAll( Gen.choose(0, Int.MaxValue) :| "x", Gen.choose(0, Int.MaxValue) :| "y") { case (x, y) => add(x, y) >= x && add(x, y) >= y}

scala> p.check ! Falsified after 0 passed tests. > x: 195667048 > x_ORIGINAL: 1350546201 > y: 1951816600 > y_ORIGINAL: 2004457204

val p = Prop.forAll( Gen.choose(0, Int.MaxValue) :| "x", Gen.choose(0, Int.MaxValue) :| "y") { case (x, y) => add(x, y) >= x && add(x, y) >= y}

scala> p.check ! Falsified after 0 passed tests. > x: 195667048 > x_ORIGINAL: 1350546201 > y: 1951816600 > y_ORIGINAL: 2004457204

195667048 + 1951816600 == Int.MaxValue + 1

val p = Prop.forAll( Gen.choose(0, Int.MaxValue) :| "x", Gen.choose(0, Int.MaxValue) :| "y") { case (x, y) => add(x, y) >= x && add(x, y) >= y}

scala> p.check ! Falsified after 0 passed tests. > x: 195667048 > x_ORIGINAL: 1350546201 > y: 1951816600 > y_ORIGINAL: 2004457204

195667048 + 1951816600 == Int.MaxValue + 1

val genListPosNum = Gen.listOf(Gen.choose(1, Int.MaxValue))val p = Prop.forAll(genListPosNum :| "positive numbers") { xs: List[Int] => xs.sorted.foldLeft(Option(0)) { case (mp, x) => mp.flatMap(p => if (p < x) Some(x) else None) }.isDefined}

scala> p.check ! Falsified after 6 passed tests. > positive numbers: List("1", "1") > positive numbers_ORIGINAL: List("1901315974", "963110019", "991102462")

val genListPosNum = Gen.listOf(Gen.choose(1, Int.MaxValue))val p = Prop.forAll(genListPosNum :| "positive numbers") { xs: List[Int] => xs.sorted.foldLeft(Option(0)) { case (mp, x) => mp.flatMap(p => if (p < x) Some(x) else None) }.isDefined}

scala> p.check ! Falsified after 6 passed tests. > positive numbers: List("1", "1") > positive numbers_ORIGINAL: List("1901315974", "963110019", "991102462")

val genListPosNum = Gen.listOf(Gen.choose(1, Int.MaxValue))val p = Prop.forAll(genListPosNum :| "positive numbers") { xs: List[Int] => xs.sorted.foldLeft(Option(0)) { case (mp, x) => mp.flatMap(p => if (p < x) Some(x) else None) }.isDefined}

scala> p.check ! Falsified after 6 passed tests. > positive numbers: List("1", "1") > positive numbers_ORIGINAL: List("1901315974", "963110019", "991102462")

class SampleTest extends Specification with ScalaCheck {

"Integer" should { "have a max method" in { Prop.forAll { (x: Int, y: Int) => val z = math.max(x, y) (z == x || z == y) && (z >= x && z >= y) } }

}

trait Monoid[A] { def op(a1: A, a2: A): A def zero: A object Laws { import scalaio.scalacheck.Eq import Eq._ def monoidIsAssociative(in: Gen[(A, A, A)])(implicit eq: Eq[A]): Prop = Prop.forAll(in) { case (x, y, z) => op(op(x, y), z) === op(x, op(y, z)) } def monoidHaveZero(in: Gen[A])(implicit eq: Eq[A]): Prop = Prop.forAll(in) { a => (op(a, zero) === a) && (op(zero, a) === a)} }}

val stringMonoid = new Monoid[String] { override def zero = "" override def op(a1: String, a2: String) = a1 + a2} def listMonoid[A] = new Monoid[List[A]] { override def op(a1: List[A], a2: List[A]) = a1 ++ a2 override def zero = List.empty[A] } val intAdditionMonoid = new Monoid[Int] { override def op(a1: Int, a2: Int) = a1 + a2 override def zero = 0 } val intMultiplicationMonoid = new Monoid[Int] { override def op(a1: Int, a2: Int) = a1 * a2 override def zero = 1 }

def endoMonoid[A] = new Monoid[A => A] { override def op(a1: A => A, a2: A => A) = { a: A => a1(a2(a)) } override def zero = identity}

def productMonoid[A, B](a: Monoid[A], b: Monoid[B]): Monoid[(A, B)] = new Monoid[(A, B)] { override def op(a1: (A, B), a2: (A, B)) = (a.op(a1._1, a2._1), b.op(a1._2, a2._2)) override def zero = (a.zero, b.zero)} def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] = new Monoid[Map[K, V]] { override def op(a1: Map[K, V], a2: Map[K, V]) = a1.map { case (k, v) => (k, V.op(v, a2.get(k) getOrElse V.zero)) } override def zero = Map()}

def parReduce[A](as: List[A]) (implicit M: Monoid[A]): A = ???

def parMapReduce[A, B](as: List[A], f: A => B) (implicit M: Monoid[B]): B = ???

"String Monoid" should { "be associative" in { val genTriple = arbitrary[(String, String, String)] Monoids.stringMonoid.Laws.monoidIsAssociative(genTriple) } "have a zero" in { Monoids.stringMonoid.Laws.monoidHaveZero(arbitrary[String]) }}

trait Eq[A] { def equals(a1: A, a2: A): Prop} object Eq { implicit def orderedEq[A: Ordering]: Eq[A] = new Eq[A] { override def equals(a1: A, a2: A) = implicitly[Ordering[A]].equiv(a1, a2) } implicit def functionEq[A: Arbitrary, B]: Eq[A => B] = new Eq[A => B] { override def equals(f1: (A) => B, f2: (A) => B) = Prop.forAll(arbitrary[A]) { a => f1(a) == f2(a) } } implicit def listEq[A]: Eq[List[A]] = new Eq[List[A]] { override def equals(a1: List[A], a2: List[A]) = a1 == a2 } implicit class EqOps[A: Eq](a1: A) { def ===(a2: A): Prop = implicitly[Eq[A]].equals(a1, a2) }}

Functional Programming in ScalaPaul Chiusano and Rúnar Bjarnason

Manning

ScalaCheckThe Definitive Guide

Richard Nilsonartima

top related