I am attempting to create a property-based test in a Scala Test FlatSpec that uses the ScalaCheck :|
operator to give failure messages for different parts of the ending boolean expression.
However, I am running into an issue where the &&
operator does not short circuit. In this case the earlier part of the expression checks to see if the next part of the expression can be run, otherwise that later section would throw an exception.
Here is an example of what the issue looks like. If decoded
is None
, then the expression should short circuit on the &&
so that decoded.get
is not run, as it would throw an exception.
val value: Array[Int] = Array.fill(2)(0)
val encoded = encode(value)
val decoded: Option[Array[Int]] = decode(value)
decoded.isDefined :| "decoded None" &&
value.sameElements(decoded.get)
When I write the boolean without using the :|
operator to give a failure message, the test fails on the decoded.isDefined
without throwing an exception.
val value: Array[Int] = Array.fill(2)(0)
val encoded = encode(value)
val decoded: Option[Array[Int]] = decode(value)
decoded.isDefined &&
value.sameElements(decoded.get)
However, when I include a failure message with :|
, it fails with a NoSuchElementException
on the value.sameElements(decoded.get)
line and does not display the failure message for decoded.isDefined
even though it would evaluate to false.
The imports and test class declaration I am using are the following:
import org.scalacheck.Prop._
import org.scalatest.prop.Checkers
import org.scalatest.{FlatSpec, Matchers}
class ByteRWTests extends FlatSpec with Matchers with Checkers {
I am writing property checks in the following manner:
it should "be equal" in {
check(
forAll { (int: Int) =>
int == int
}
)
}
Is there any way to get the short circuiting for &&
to work with expressions using :|
, or is there a workaround for this issue?
The issue is that while the &&
for booleans is short-circuiting, the &&
method on Prop
isn't, and whether or not you use a label determines where the implicit conversion from boolean to Prop
happens. For example:
import org.scalacheck.Prop, Prop._
val value: Array[Int] = Array.fill(2)(0)
val decoded: Option[Array[Int]] = None
val p1: Prop = decoded.isDefined && value.sameElements(decoded.get)
val p2: Prop = decoded.isDefined :| "decoded None" && value.sameElements(decoded.get)
Here the p1
definition desugars to this:
Prop.propBoolean(decoded.isDefined && value.sameElements(decoded.get))
While p2
gives you this:
(Prop.propBoolean(decoded.isDefined) :| "decoded None").&&(
Prop.propBoolean(value.sameElements(decoded.get))
)
(For what it's worth this is another example of why I don't like fancy implicit-conversion-based DSLs.)
Unfortunately it's just not possible to get the &&
method on Prop
to do what you want here, but you can define your own version of conjunction that does:
def propAnd(p1: => Prop, p2: => Prop) = p1.flatMap { r =>
if (r.success) Prop.secure(p2) else Prop(_ => r)
}
And then:
scala> propAnd(decoded.isDefined :| "decoded None" , value.sameElements(decoded.get))
res1: org.scalacheck.Prop = Prop
scala> .check
! Falsified after 0 passed tests.
> Labels of failing property:
decoded None
This method actually exists in ScalaCheck, but it's not part of the public API. A comment on the implementation does note that it "(Should maybe be in Prop module)", so if you find you're doing this kind of thing a lot, you might try opening a pull request to move the method from Commands
to Prop
and make it public.
In the specific case you've given here, though, I'd probably suggest using something like exists
on the Option
instead of checking whether it's defined and then using get
.