scalabling - 4 years ago 101
Scala Question

# ScalaCheck valid/invalid test boundary

I'm using ScalaCheck to do some property-based tests in ScalaTest. Say I want to test a function,

`f(x: Double): Double`
that is only defined for
`x >= 0.0`
, and which returns
`NaN`
for arguments outside of that domain. Ideally, I'd like to do something like this:

``````import org.scalatest.FunSpec
import org.scalatest.prop.GeneratorDrivenPropertyChecks

def f(x: Double) = Math.sqrt(x) // The actual function isn't important.

class FTest
extends FunSpec
with GeneratorDrivenPropertyChecks {
describe("f(x)") {
it("must accept every argument value and handle it correctly") {
forAll { x: Double =>
val r = f(x)
if(x >= 0.0) assert(!r.isNaN && r === Math.sqrt(x)) // Too simplistic, I know. ;-)
else assert(r.isNaN)
}
}
}
}
``````

Now, this is fairly elegant and works, but I'm concerned about boundary checking, because I doubt that - in the general case - ScalaCheck is going to be able to find the boundary and test that the function responds correctly with the values either side of that boundary (>= 0.0 in this case). Of course, I can separate the two conditions using
`whenever`
(ScalaTest's replacement for ScalaCheck's
`==>`
operator), but that's more effort and a lot of generated values are wasted:

``````class FTest2
extends FunSpec
with GeneratorDrivenPropertyChecks {
describe("f(x)") {
it("must accept every valid argument value and handle it correctly") {
forAll { x: Double =>
whenever(x >= 0.0) {
val r = f(x)
assert(!r.isNaN && r === Math.sqrt(x))
}
}
}
it("must report the correct error value for invalid argument values") {
forAll { x: Double =>
whenever(x < 0.0) assert(f(x).isNaN)
}
}
}
}
``````

(I know that I can also use a customer generator to limit the range so that
`whenever`
is not required, but I think that's beside the point. Feel free to correct me if I'm wrong about that.)

So, what I'm curious about is:

1. Is there a way to hint to ScalaCheck what the boundary value is, and ensure that it picks that value and the values either side of it?

2. Is there any alternative to this that is equally as elegant, but which does a better job of finding the boundary automatically?

ScalaCheck cannot automatically figure out which values your function treats as valid; you need to either encode this information in your properties (using something like `whenever`) or in your generators. Which approach to pick is context-specific.

Keeping properties "small" is preferable: focused, orthogonal properties are easier to read/write/maintain, and you can always compose them later to build more comprehensive properties. Therefore, I would keep the two properties (happy and unhappy cases) separate.

To avoid "wasting" generated values, I would use two separate generators (one for non-negative doubles and another for negative doubles); no need for `whenever` with that approach.

``````val genNonnegativeDouble: Gen[Double] = Gen.choose(0, Double.MaxValue)

val genNegativeDouble: Gen[Double] = Gen.negNum[Double]
``````

Your properties would then look as follows:

``````final class FTest2
extends FunSpec
with GeneratorDrivenPropertyChecks {

describe("f") {
it("must accept every valid argument value and handle it correctly") {
forAll(genNonnegativeDouble) { x =>
val r = f(x)
assert(!r.isNaN && r === Math.sqrt(x))
}
}

it("must report the correct error value for invalid argument values") {
forAll(negativeDouble) { x =>
assert(f(x).isNaN)
}
}
}

}
``````

Incidentally,

• You should probably return something other than `Double.NaN` to indicate failure; an `Option[Double]` is a good candidate, here, as there is only one reason for failure.
• You should only check doubles for approximate equality.
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download