Saturday, January 17, 2015

Automatic Boundary Checking


Surprises in Java's Math class

ScalaTest/ScalaCheck has a rather nice little feature. It can boundary check your unit tests. Let's take this code:

import org.scalatest.WordSpec
import org.scalatest.prop.GeneratorDrivenPropertyChecks
import org.scalatest.Matchers

class MyScalaTest extends WordSpec with GeneratorDrivenPropertyChecks with Matchers {

  "java.lang.Math.abs" should {
    "always return a non-negative value" in {
      forAll { x: Int =>
        Math.abs(x) should be >= 0
      }
    }
  }
}

It nicely runs the test many times, probing it with different values. Unfortunately, it fails with:

[info] MyScalaTest:
[info] Math.abs
[info] - should always return a non-negative value *** FAILED ***
[info]   TestFailedException was thrown during property evaluation.
[info]     Message: -2147483648 was not greater than or equal to 0
[info]     Location: (MyScalaTest.scala:12)
[info]     Occurred when passed generated values (
[info]       arg0 = -2147483648
[info]     )

What? How could java.lang.Math.abs fail to return a non-negative value? Well, we hit a boundary condition. ScalaCheck is clever enough to try Integer.MIN_VALUE. However, because there are more negative Integers than positive (the range is -231 to 231-1), not all negative integers map to positive ones.

If we are happy with a limited range, we can write:

import org.scalacheck.Gen
.
.     
      val sensibleRange = Gen.choose(0, 100)

      forAll(sensibleRange) {
.
.

to choose numbers inclusive of 0 to 99.

No comments:

Post a Comment