property based testing with scalacheck

55
Qspqfsuz!Cbtfe!Uftujoh! xjui!TdbmbDifdl Ebwje!Hbmjdifu! Gsffmbodf!Gvodujpobm!Qsphsbnnfs! Uxjuufs;!Aehbmjdifu

Upload: david-galichet

Post on 04-Jun-2015

477 views

Category:

Presentations & Public Speaking


2 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Property Based Testing with ScalaCheck
Page 2: Property Based Testing with ScalaCheck
Page 3: Property Based Testing with ScalaCheck
Page 4: Property Based Testing with ScalaCheck
Page 5: Property Based Testing with ScalaCheck
Page 6: Property Based Testing with ScalaCheck
Page 7: Property Based Testing with ScalaCheck
Page 8: Property Based Testing with ScalaCheck
Page 9: Property Based Testing with ScalaCheck
Page 10: Property Based Testing with ScalaCheck
Page 11: Property Based Testing with ScalaCheck
Page 12: Property Based Testing with ScalaCheck

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.

Page 13: Property Based Testing with ScalaCheck

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.

Page 14: Property Based Testing with ScalaCheck

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.

Page 15: Property Based Testing with ScalaCheck

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.

Page 16: Property Based Testing with ScalaCheck

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.

Page 17: Property Based Testing with ScalaCheck
Page 18: Property Based Testing with ScalaCheck

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

Page 19: Property Based Testing with ScalaCheck

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

Page 20: Property Based Testing with ScalaCheck

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

Page 21: Property Based Testing with ScalaCheck

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)

Page 22: Property Based Testing with ScalaCheck

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

Page 23: Property Based Testing with ScalaCheck

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

Page 24: Property Based Testing with ScalaCheck

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

Page 25: Property Based Testing with ScalaCheck

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

Page 26: Property Based Testing with ScalaCheck

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

Page 27: Property Based Testing with ScalaCheck
Page 28: Property Based Testing with ScalaCheck
Page 29: Property Based Testing with ScalaCheck

Int,String… Int, String…

Page 30: Property Based Testing with ScalaCheck

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

Page 31: Property Based Testing with ScalaCheck

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

Page 32: Property Based Testing with ScalaCheck

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

Page 33: Property Based Testing with ScalaCheck

import org.scalacheck.Prop.BooleanOperators

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

Page 34: Property Based Testing with ScalaCheck

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.

Page 35: Property Based Testing with ScalaCheck

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.

Page 36: Property Based Testing with ScalaCheck

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

Page 37: Property Based Testing with ScalaCheck

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

Page 38: Property Based Testing with ScalaCheck

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

Page 39: Property Based Testing with ScalaCheck

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")

Page 40: Property Based Testing with ScalaCheck

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")

Page 41: Property Based Testing with ScalaCheck

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")

Page 42: Property Based Testing with ScalaCheck

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) } }

}

Page 43: Property Based Testing with ScalaCheck
Page 44: Property Based Testing with ScalaCheck
Page 45: Property Based Testing with ScalaCheck
Page 46: Property Based Testing with ScalaCheck
Page 47: Property Based Testing with ScalaCheck
Page 48: Property Based Testing with ScalaCheck

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)} }}

Page 49: Property Based Testing with ScalaCheck

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}

Page 50: Property Based Testing with ScalaCheck

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()}

Page 51: Property Based Testing with ScalaCheck

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 = ???

Page 52: Property Based Testing with ScalaCheck

"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]) }}

Page 53: Property Based Testing with ScalaCheck

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) }}

Page 54: Property Based Testing with ScalaCheck

Functional Programming in ScalaPaul Chiusano and Rúnar Bjarnason

Manning

ScalaCheckThe Definitive Guide

Richard Nilsonartima

Page 55: Property Based Testing with ScalaCheck