Properties

A property is the testable unit in ScalaCheck, and is represented by the org.scalacheck.Prop class. There are several ways to create properties in ScalaCheck, one of them is to use the org.scalacheck.Prop.forAll method like in the example below.

scala> val propConcatLists = forAll { (l1: List[Int], l2: List[Int]) =>
  l1.size + l2.size == (l1 ::: l2).size }

We can use the check method to test it:

scala> propConcatLists.check
+ OK, passed 100 tests.

OK, that seemed alright. Now, we'll define another property.

scala> val propSqrt = forAll { (n: Int) => scala.math.sqrt(n*n) == n }

And check it:

scala> propSqrt.check
! Falsified after 2 passed tests.
> ARG_0: -1
> ARG_0_ORIGINAL: -488187735

Not surprisingly, the property doesn't hold. The argument -1 falsifies it. You can also see that the argument -488187735 falsifies the property. That was the first argument ScalaCheck found, and it was then simplified to -1.

The forAll method creates universally quantified properties directly, but it is also possible to create new properties by combining other properties, or to use any of the specialised methods in the org.scalacheck.Prop object.

Universally quantified properties

As mentioned before, org.scalacheck.Prop.forAll creates universally quantified properties. forAll takes a function as parameter, and creates a property out of it that can be tested with the check method or with Scalatest (using Checkers trait), like in these examples.

The function passed to forAll should return Boolean or another property, and can take parameters of any types, as long as there exist implicit Arbitrary instances for the types. By default, ScalaCheck has instances for common types like Int, String, List, etc, but it is also possible to define your own Arbitrary instances.

For example:

import org.scalacheck.Prop.forAll

check {
  forAll((s1: String, s2: String) => (s1 + s2).endsWith(s2) == res0)
}

When you run check on the properties, ScalaCheck generates random instances of the function parameters and evaluates the results, reporting any failing cases.

You can also give forAll a specific data generator. In the following example smallInteger defines a generator that generates integers between 0 and 100, inclusively.

This way of using the forAll method is good to use when you want to control the data generation by specifying exactly which generator that should be used, and not rely on a default generator for the given type.

import org.scalacheck.Gen
import org.scalacheck.Prop.forAll

val smallInteger = Gen.choose(0, 100)

check {
  forAll(smallInteger)(n => (n >= 0 && n <= 100) == res0)
}

Conditional properties

Sometimes, a specification takes the form of an implication. In ScalaCheck, you can use the implication operator ==> to filter the generated values.

If the implication operator is given a condition that is hard or impossible to fulfill, ScalaCheck might not find enough passing test cases to state that the property holds. In the following trivial example, all cases where n is non-zero will be thrown away:

scala> import org.scalacheck.Prop.{forAll, propBoolean}
scala> val propTrivial = forAll { n: Int =>
     |  (n == 0) ==> n + 10 == 10
     | }

scala> propTrivial.check
! Gave up after only 4 passed tests. 500 tests were discarded.

It is possible to tell ScalaCheck to try harder when it generates test cases, but generally you should try to refactor your property specification instead of generating more test cases, if you get this scenario.

Using implications, we realise that a property might not just pass or fail, it could also be undecided if the implication condition doesn't get fulfilled.

In this example, ScalaCheck will only care for the cases when n is an even number.

import org.scalacheck.Prop.{ forAll, propBoolean }

check {
  forAll { n: Int => (n % 2 == 0) ==> (n % 2 == res0) }
}

Combining Properties

A third way of creating properties, is to combine existing properties into new ones.

val p1 = forAll(...)
val p2 = forAll(...)
val p3 = p1 && p2
val p4 = p1 || p2
val p5 = p1 == p2
val p6 = all(p1, p2) // same as p1 && p2
val p7 = atLeastOne(p1, p2) // same as p1 || p2

Here, p3 will hold if and only if both p1 and p2 hold, p4 will hold if either p1 or p2 holds, and p5 will hold if p1 holds exactly when p2 holds and vice versa.

import org.scalacheck.Gen
import org.scalacheck.Prop.forAll

val smallInteger = Gen.choose(0, 100)

check {
  forAll(smallInteger) { n =>
    (n > 100) == res0
  } &&
    forAll(smallInteger)(n => (n >= 0) == res1)
}

Grouping Properties

Often you want to specify several related properties, perhaps for all methods in a class. ScalaCheck provides a simple way of doing this, through the Properties trait. Look at the following specifications that define some properties for zero.

You can use the check method of the Properties class to check all specified properties, just like for simple Prop instances. In fact, Properties is a subtype of Prop, so you can use it just as if it was a single property.

That single property holds if and only if all of the contained properties hold.

import org.scalacheck.{ Prop, Properties }

class ZeroSpecification extends Properties("Zero") {

  import org.scalacheck.Prop.{ forAll, propBoolean }

  property("addition property") = forAll { n: Int => (n != 0) ==> (n + res0 == n) }

  property("additive inverse property") = forAll { n: Int => (n != 0) ==> (n + (-n) == res1) }

  property("multiplication property") = forAll { n: Int => (n != 0) ==> (n * res2 == 0) }

}

check(Prop.all(new ZeroSpecification().properties.to(List).map(_._2): _*))