ScalaCheck有效/无效的测试边界

时间:2017-02-03 05:57:09

标签: scala scalatest scalacheck

我正在使用 ScalaCheck ScalaTest 中进行一些基于属性的测试。假设我想测试一个仅为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)
      }
    }
  }
}

现在,这是相当优雅和工作,但我担心边界检查,因为我怀疑 - 在一般情况下 - ScalaCheck将能够找到边界并测试该函数正确响应值该边界的任一侧(在这种情况下,> = 0.0)。当然,我可以使用whenever ScalaTest 替换 ScalaCheck ==>运算符)来区分这两个条件,但这需要更多努力并浪费了很多生成的值:

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)
      }
    }
  }
}

(我知道我也可以使用客户生成器来限制范围,因此不需要whenever,但我认为这不是重点。如果我错了,请随意纠正我。 )

所以,我很好奇的是:

  1. 有没有办法提示ScalaCheck边界值是什么,并确保它选择该值及其两侧的值?
  2. 除此之外是否有任何其他选择同样优雅,但是它能更好地自动找到边界?
  3. 感谢您的帮助〜

1 个答案:

答案 0 :(得分:2)

ScalaCheck无法自动确定您的功能视为有效的值;您需要在属性中编码此信息(使用类似whenever的内容)或在您的生成器中编码。选择哪种方法是特定于上下文的。

保持属性“小”是优选的:聚焦,正交属性更易于读/写/维护,您可以随后组合它们以构建更全面的属性。因此,我会将两个属性(快乐和不快乐的情况)分开。

为了避免“浪费”生成的值,我会使用两个独立的生成器(一个用于非负双打,另一个用于负双打);这种方法不需要whenever

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

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

您的属性将如下所示:

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)
      }
    }
  }

}

顺便提及,

  • 您应该返回Double.NaN以外的其他内容来表示失败; Option[Double]是一个很好的候选人,因为只有一个失败原因。
  • 您只应检查双打近似相等。