当前位置: 首页 > 知识库问答 >
问题:

正数上的属性不应该缩小到负数

宋昕
2023-03-14

在 ScalaCheck 中,我有一个属性测试,它使用正整数生成器,当它失败时,ScalaCheck 将缩小到非正值。

收缩应该有助于找到最小的失败案例。收缩到所需范围之外的值会令人困惑且无济于事。这是一个已知的错误,请参阅ScalaCheck问题#129Gen.suchThat不尊重收缩

是否可以在作用域中定义我自己的收缩实例,以便它仅收缩到正整数?

下面是一个最简单的属性测试:

class ShrinkProp extends Properties("Shrink") {
  property("posNum[Int]") = {
    Prop.forAll(Gen.posNum[Int]) { _: Int =>
      Prop.falsified
    }
  }
}

这通常会导致 ScalaCheck 将参数缩小到零:

[info] Done compiling.
[info] ! Shrink.posNum[Int]: Falsified after 0 passed tests.
[info] > ARG_0: 0
[info] > ARG_0_ORIGINAL: 1
[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

或者更糟糕的是,它有时可能会缩小到负值:

[info] ! Shrink.posNum[Int]: Falsified after 5 passed tests.
[info] > ARG_0: -1
[info] > ARG_0_ORIGINAL: 3
[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

一种解决方案是使用< code>forAllNoShrink关闭收缩:

class ShrinkProp extends Properties("Shrink") {
  property("posNum[Int]") = {
    Prop.forAllNoShrink(Gen.posNum[Int]) { _: Int =>
      Prop.falsified
    }
  }
}

结果是没有缩小:

[info] ! Shrink.posNum[Int]: Falsified after 0 passed tests.
[info] > ARG_0: 1
[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

另一种选择是在测试中添加一个保护,以便缩小值或跳过:

import Prop.BooleanOperators

class ShrinkProp extends Properties("Shrink") {
  property("posNum[Int]") = {
    Prop.forAll(Gen.posNum[Int]) { x: Int =>
      (x >=  1) ==> Prop.falsified
    }
  }
}

除了禁用收缩和添加防护外,还有其他选择吗?

共有1个答案

杜俭
2023-03-14

在 Scala 检查中没有正整数的收缩器。你必须写你自己的。

收缩需要定义为属性测试范围内的隐式内容。然后,Prop.forAll 将找到正确的收缩类(如果它在作用域内,并且具有未通过测试的值的相应类型签名)。

从根本上讲,Shrink实例是一个函数,它将失败的值x

trait Shrink[T] {
  def shrink(x: T): Stream[T]
}

您可以使用伴随对象的 apply 方法定义 Shrink,大致如下:

object Shrink {
  def apply[T](s: T => Stream[T]): Shrink[T] = {
    new Shrink[T] {
      def shrink(x: T): Stream[T] = s(x)
    }
  }
}

正整数的收缩器是一种,它通过对值减半来收缩,以通过二进制搜索找到最小的失败情况,但在达到零之前停止:

class ShrinkProp extends Properties("Shrink") {

  implicit val posIntShrinker: Shrink[Int] = Shrink { x: Int =>
    Stream.iterate(x / 2) { x: Int =>
      x / 2
    }.takeWhile { x: Int =>
      x > 0 // Avoid zero.
    }
  }

  property("posNum[Int]") = {
    Prop.forAll(Gen.posNum[Int]) { _: Int =>
      Prop.falsified
    }
  }
}

证明故障正在起作用:


[info] ! Shrink.posNum[Int]: Falsified after 6 passed tests.
[info] > ARG_0: 2
[info] > ARG_0_ORIGINAL: 4
[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

更好的是,您可以编写一个属性来验证收缩器的行为是否正常:

property("posIntShrinker") = {
  Prop.forAll { x: Int =>
    val shrunk = Shrink.shrink(x)
    Prop.atLeastOne(
      (x >= 2) ==> shrunk.size > 0,
      (x <= 1) ==> shrunk.isEmpty
    )
  }
}

[info] + Shrink.posIntShrinker: OK, passed 100 tests.
[info] Failed: Total 1, Failed 0, Errors 0, Passed 1

最好编写一个通用正数Shrink,它能够收缩其他类型的数字,例如Long、浮点类型和BigDecimal

 类似资料:
  • 问题内容: 当我在终端尝试这个 我收到以下错误 但是,我可以分两个步骤执行此操作,例如, 为什么会有这种行为?用单行解决此问题的方法是什么? 问题答案: 提高功率优先于一元减号。 因此,您拥有的却不是您所期望的: 如果您希望它起作用,您应该写 或按照@TimPietzcker的说明切换Python 3。

  • 我在下有一个测试属性文件。但是我无法在我的单元测试中获取要加载的值。我有以下类: 在生产代码中,加载来自src/main/resources/application的值。yml。 单元测试等级: 我试图为每个应用程序添加一个测试配置文件。yml文件,看看添加ActiveProfiles(“test”)是否有效,但没有。 src/main/resources/application。yml公司 我还

  • 在Grails中,当子类可以属于父类中的两个属性之一(但不能同时属于两个属性)时,我很难理解Belongsto-HasMany关系的概念。 例如: 其中Person位于Store.Employees列表或Store.Managers列表中 事先谢谢....

  • 问题内容: 我正在关注Laracasts的视频:基本模型/控制器/视图工作流程。 我的桌子上有联系方式。 我正在尝试使用控制器文件中的以下代码传递数据以进行查看: 当我尝试显示如下所示的代码时, 我收到错误提示: 该集合实例上不存在属性[title]。(视图:E:\ laragon \ www \ newsite \ resources \ views \ about.blade.php) 但是,

  • 问题内容: Facelets使用 jsfc* 属性将HTML元素转换为其关联的 JSF 组件。这对于快速原型制作非常有用,因为它允许您使用视觉设计工具创建视图。但是,我最近发现了 Cay Horstmann的 这篇 博客文章,他浪费了 jsfc 以及 h:dataTable 等复杂组件的使用。 __ *** 这让我感到震惊,因为 Cay Horstmann 是我最喜欢的Java书籍的多本作者。但是

  • 问题内容: 我正在尝试从解码为PHP对象的JSON数据中获取一个属性。这只是一个YouTube数据API请求,它返回具有内容对象的视频对象。 在做 抛出“意外的T_DNUMBER”-这很合理。但是,如何获得数字属性的值? 我确定我应该知道这一点。提前致谢。 问题答案: 这应该工作: