ScalaCheck valid/invalid 测试边界
ScalaCheck valid/invalid test boundary
我正在使用 ScalaCheck 在 ScalaTest 中做一些基于 属性 的测试。假设我想测试一个函数 f(x: Double): Double
,它只为 x >= 0.0
定义,而 returns 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
,但我认为这不是重点。如果我错了,请随时纠正我那个。)
所以,我很好奇的是:
- 有没有办法向 ScalaCheck 提示边界值是什么,并确保它选择该值及其两侧的值?
- 是否有其他替代方案同样优雅,但自动找到边界的效果更好?
感谢您的帮助~
ScalaCheck 无法自动确定您的函数将哪些值视为有效;您需要在您的属性(使用 whenever
之类的东西)或您的生成器中对这些信息进行编码。选择哪种方法是 context-specific.
保持属性 "small" 更可取:集中的正交属性更容易 read/write/maintain,您以后可以随时组合它们以构建更全面的属性。因此,我会将这两个属性(快乐和不快乐的情况)分开。
为了避免 "wasting" 生成值,我会使用两个单独的生成器(一个用于 non-negative 双精度,另一个用于负双精度);这种方法不需要 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)
}
}
}
}
顺便说一句,
- 你可能应该 return 不同于
Double.NaN
的东西来表示失败; Option[Double]
是一个很好的候选人,因为失败的原因只有一个。
- 您应该只检查 近似 相等的双打。
我正在使用 ScalaCheck 在 ScalaTest 中做一些基于 属性 的测试。假设我想测试一个函数 f(x: Double): Double
,它只为 x >= 0.0
定义,而 returns 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
,但我认为这不是重点。如果我错了,请随时纠正我那个。)
所以,我很好奇的是:
- 有没有办法向 ScalaCheck 提示边界值是什么,并确保它选择该值及其两侧的值?
- 是否有其他替代方案同样优雅,但自动找到边界的效果更好?
感谢您的帮助~
ScalaCheck 无法自动确定您的函数将哪些值视为有效;您需要在您的属性(使用 whenever
之类的东西)或您的生成器中对这些信息进行编码。选择哪种方法是 context-specific.
保持属性 "small" 更可取:集中的正交属性更容易 read/write/maintain,您以后可以随时组合它们以构建更全面的属性。因此,我会将这两个属性(快乐和不快乐的情况)分开。
为了避免 "wasting" 生成值,我会使用两个单独的生成器(一个用于 non-negative 双精度,另一个用于负双精度);这种方法不需要 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)
}
}
}
}
顺便说一句,
- 你可能应该 return 不同于
Double.NaN
的东西来表示失败;Option[Double]
是一个很好的候选人,因为失败的原因只有一个。 - 您应该只检查 近似 相等的双打。