Property based testing
- Different way of testing
- Declarative
- Logic properties
- Abstraction over values
- Declarative way to say what the unit under testing should do
- Think in logic formula's
- Abstraction over values
Properties
$\forall s : String \rightarrow s.reverse.reverse \equiv s$
or:
forAll((s: String) => s.reverse.reverse == s)
+ String.should be the same after reversing twice: OK, passed 100 tests.
ScalaCheck
Checking properties on random datasets created by generators
- Property-based testing for Scala
-
Inspired on Haskell's
QuickCheck
- Generates test data with corner cases
-
Guides programmer to writing pure functions (TTD with property based checking)
- Can be used in interactively in REPL
-
Can be used to test Java code (or any JVM-language for that matter)
- Works autonomously or with ScalaTest or Specs2
- Shrinking strategies to reduce the counter example
Simple Example
def reverseStrings(list: List[String]): List[String] = {
list match {
case Nil => Nil
case (x :: xs) => reverseStrings(xs) ++ List(x)
}
}
ScalaCheck Autonomous
property("should be the same after reversing twice") =
forAll((ss: List[String]) => reverseStrings(reverseStrings(ss)) == ss)
ScalaTest with ScalaCheck
behavior of "reverseStrings"
it should "give the same after reversing twice" in {
forAll {
(ss: List[String]) =>
reverseStrings(reverseStrings(ss)) should equal(ss)
}
}
Property Test Strategies
- Do not redo your implementation in your test
- Test multiple (trivial) properties
-
Think backwards (generate the output: $\forall l : SortedList \rightarrow shuffle(l).sort \equiv l$)
-
Use a slower, different or proven way to find the correct answers (- maybe your previous implementation that you are improving)
Generators
Generators for primitive types and case classes are provided
Or roll your own:
def bbans: Gen[String] =
for {
n <- Gen.chooseNum(0, 9999999)
} yield n.toString
Concluding
- Property based checking with ScalaCheck
- Extra tool in testing tool belt
- Can test non-Scala code