scalabling - 4 years ago 101

Scala Question

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

`f(x: Double): Double`

`x >= 0.0`

`NaN`

`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`

`==>`

`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`

So, what I'm curious about is:

- 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?
- Is there any alternative to this that is equally as elegant, but which does a better job of finding the boundary automatically?

Thanks for your assistance~

Recommended for you: Get network issues from **WhatsUp Gold**. **Not end users.**

Answer Source

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**