与 :| 发生 && 短路在 ScalaCheck 的表达式中

Have && short circuit with :| in expressions in ScalaCheck

我正在尝试在 Scala Test FlatSpec 中创建一个基于 属性 的测试,它使用 ScalaCheck :| 运算符为结束布尔表达式的不同部分提供失败消息。

但是,我 运行 遇到 && 运算符不会短路的问题。在这种情况下,表达式的前面部分检查表达式的下一部分是否可以是 运行,否则后面的部分将抛出异常。

这是问题的示例。如果 decodedNone,那么表达式应该在 && 上短路,这样 decoded.get 就不是 运行,因为它会抛出异常。

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)

当我在不使用 :| 运算符给出失败消息的情况下编写布尔值时,测试在 decoded.isDefined 上失败而没有抛出异常。

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)

但是,当我包含带有 :| 的失败消息时,它失败并在 value.sameElements(decoded.get) 行显示 NoSuchElementException,并且不显示 decoded.isDefined 的失败消息即使它的计算结果为 false。

我使用的导入和测试 class 声明如下:

import org.scalacheck.Prop._
import org.scalatest.prop.Checkers
import org.scalatest.{FlatSpec, Matchers}

class ByteRWTests extends FlatSpec with Matchers with Checkers {

我正在按以下方式编写 属性 检查:

it should "be equal" in {
  check(
    forAll { (int: Int) =>
      int == int
    }
  )
}

是否有任何方法可以使 && 短路以使用 :| 处理表达式,或者是否有解决此问题的方法?

为什么你看到了差异

问题在于,布尔值的 && 是短路的,而 Prop 上的 && 方法不是,是否使用标签决定了在哪里从布尔值到 Prop 的隐式转换发生了。例如:

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)

这里的 p1 定义脱糖为:

Prop.propBoolean(decoded.isDefined && value.sameElements(decoded.get))

虽然 p2 给你这个:

(Prop.propBoolean(decoded.isDefined) :| "decoded None").&&(
  Prop.propBoolean(value.sameElements(decoded.get))
)

(值得一提的是 的另一个例子。)

解决方法

不幸的是,不可能在 Prop 上使用 && 方法来做你想做的事,但你可以定义你自己的连词版本:

def propAnd(p1: => Prop, p2: => Prop) = p1.flatMap { r =>
  if (r.success) Prop.secure(p2) else Prop(_ => r)
}

然后:

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

这个方法在ScalaCheck中确实存在,只不过是not part of the public API。对实现的评论确实注意到它“(应该在 Prop 模块中)”,所以如果你发现你经常做这种事情,你可以尝试打开一个拉取请求以将方法从 CommandsProp 并使其成为 public.

不过,在你在这里给出的具体情况下,我可能建议在 Option 上使用类似 exists 的东西,而不是检查它是否已定义,然后使用 get.