property based testing with scalacheck
DESCRIPTION
From my talk at the conference Scala.IO at Paris in October 2014TRANSCRIPT
![Page 1: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/1.jpg)
![Page 2: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/2.jpg)
![Page 3: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/3.jpg)
![Page 4: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/4.jpg)
![Page 5: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/5.jpg)
![Page 6: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/6.jpg)
![Page 7: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/7.jpg)
![Page 8: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/8.jpg)
![Page 9: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/9.jpg)
![Page 10: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/10.jpg)
![Page 11: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/11.jpg)
![Page 12: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/12.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/13.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/14.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/15.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/16.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/17.jpg)
![Page 18: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/18.jpg)
class Gen[+T] { def apply(prms: Gen.Params): Option[T] }
![Page 19: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/19.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/20.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/21.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/22.jpg)
for { s <- Arbitrary.arbitrary[String] index <- Gen.choose(0, s.length)} yield (s, index)
![Page 23: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/23.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/24.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/25.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/26.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/27.jpg)
![Page 28: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/28.jpg)
![Page 29: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/29.jpg)
Int,String… Int, String…
![Page 30: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/30.jpg)
Prop.forAll { xs: List[Int] => xs.reverse.reverse == xs }
![Page 31: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/31.jpg)
Prop.forAll { xs: List[Int] => xs.reverse.reverse == xs }
![Page 32: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/32.jpg)
Prop.forAll { xs: List[Int] => xs.reverse.reverse == xs }
![Page 33: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/33.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/34.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/35.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/36.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/37.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/38.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/39.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/40.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/41.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/42.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/43.jpg)
![Page 44: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/44.jpg)
![Page 45: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/45.jpg)
![Page 46: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/46.jpg)
![Page 47: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/47.jpg)
![Page 48: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/48.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/49.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/50.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/51.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/52.jpg)
"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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/53.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/54.jpg)
Functional Programming in ScalaPaul Chiusano and Rúnar Bjarnason
Manning
ScalaCheckThe Definitive Guide
Richard Nilsonartima
![Page 55: Property Based Testing with ScalaCheck](https://reader033.vdocuments.site/reader033/viewer/2022042815/55700adcd8b42ac0178b48a6/html5/thumbnails/55.jpg)