Skip to content

Instantly share code, notes, and snippets.

@dcastro
Last active November 28, 2017 21:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcastro/b01f9c441c79a4f644705268dcbc3750 to your computer and use it in GitHub Desktop.
Save dcastro/b01f9c441c79a4f644705268dcbc3750 to your computer and use it in GitHub Desktop.
Scalacheck demo
/*
* useful links
*
* https://github.com/rickynils/scalacheck/blob/master/doc/UserGuide.md
* http://www.scalatest.org/user_guide/generator_driven_property_checks
*
*/
import org.scalacheck._
import org.scalacheck.Arbitrary._
import org.scalacheck.Prop._
val propReverse = forAll { s: String =>
s.reverse.reverse == s
}
propReverse.check
object SortProperties extends Properties("Sort") {
property("sorting is idempotent") = forAll { xs: List[Int] =>
xs.sorted.sorted == xs.sorted
}
property("sorting doesn't remove elements") = forAll { xs: List[Int] =>
xs.forall { x =>
xs.sorted.contains(x)
}
}
property("sorting doesn't add new elements") = forAll { xs: List[Int] =>
xs.sorted.forall {x =>
xs.contains(x)
}
}
property("sorting doesn't change element count") = forAll { xs: List[Int] =>
xs.length == xs.sorted.length
}
property("each elem is smaller than or equal to the next") = forAll { xs: List[Int] =>
// this property doesn't make sense for lists with less than 2 elements
(xs.length >= 2) ==> {
xs.sorted.sliding(2).forall {
case List(elem, next) => elem <= next
}
}
}
}
SortProperties.check()
import cats.implicits._
import org.scalacheck._
import org.scalacheck.Arbitrary._
import org.scalacheck.Prop._
case class Person(name: String, age: Int)
object Person {
val genPerson: Gen[Person] =
for {
name <- Gen.alphaNumStr
age <- Gen.posNum[Int]
} yield Person(name, age)
val genYoungPerson: Gen[Person] =
for {
person <- genPerson
age <- Gen.chooseNum(0, 18)
} yield person.copy(age = age)
val genOldPerson: Gen[Person] =
for {
person <- genPerson
age <- Gen.chooseNum(18, Int.MaxValue)
} yield person.copy(age = age)
val genYoungOrOldPerson: Gen[Person] =
Gen.frequency(
1 -> genYoungPerson,
2 -> genOldPerson
)
// canonical random Person
implicit val arbPerson: Arbitrary[Person] = Arbitrary(genPerson)
}
import Person._
genYoungPerson.sample
arbitrary[Person].sample
object App {
def oldest(people: List[Person]): Person =
// this is going to fail for empty lists
people.maxBy(_.age)
val p = forAll { people: List[Person] =>
val oldest: Person = App.oldest(people)
// the oldest person's age is greater than everyone else's age
people.forall { p =>
oldest.age >= p.age
}
}
}
App.p.check
// fixed version of App
object App2 {
def oldest(people: List[Person]): Option[Person] = people match {
case Nil => None
case _ => people.maxBy(_.age).some
}
val p = forAll { people: List[Person] =>
val oldest: Option[Person] = App2.oldest(people)
// the oldest person's age is greater than everyone else's age
people match {
case Nil => oldest.isEmpty
case _ => people.forall { p =>
oldest match {
case Some(o) => o.age >= p.age
case _ => false
}
}
}
}
}
App2.p.check
import org.scalacheck._
import org.scalacheck.Arbitrary._
import spray.json._
object JsGens {
val genJsString = Gen.alphaNumStr.map(s => JsString(s))
val genJsNum = arbitrary[BigDecimal] map JsNumber.apply
val genJsBool = arbitrary[Boolean] map JsBoolean.apply
val genJsNull = Gen.const(JsNull)
val genKeyValuePair: Gen[(String, JsValue)] =
for {
key <- Gen.alphaNumStr
value <- arbitrary[JsValue]
} yield (key, value)
val genJsObject: Gen[JsObject] = for {
n <- Gen.chooseNum[Int](0, 10)
pairs <- Gen.listOfN(n, genKeyValuePair)
} yield JsObject(pairs: _*)
val genJsArray: Gen[JsArray] = for {
n <- Gen.chooseNum(0, 10)
values <- Gen.listOfN(n, genJsValue)
} yield JsArray(values: _*)
def genJsValue: Gen[JsValue] =
Gen.frequency(
2 -> genJsString,
2 -> genJsBool,
2 -> genJsNum,
1 -> genJsNull,
2 -> genJsObject,
2 -> genJsArray
)
implicit def arbJsObject: Arbitrary[JsObject] = Arbitrary(genJsObject)
implicit def arbJsValue: Arbitrary[JsValue] = Arbitrary(genJsValue)
}
import JsGens._
arbitrary[JsObject].sample.map(_.prettyPrint)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment