【问题标题】:Ignore "require" and assertions during tests with ScalaCheck?在使用 ScalaCheck 进行测试期间忽略“要求”和断言?
【发布时间】:2016-11-02 10:17:08
【问题描述】:

我们的案例类有多个require 语句,用于在收到格式错误的输入时抛出异常。这通常很有帮助,但会使编写基于属性的测试变得痛苦,因为我们必须编写满足所有要求的生成器,而不是简单地使用内置的生成器。有没有一种简单的方法可以让 Scala 在测试期间忽略 require 语句?

【问题讨论】:

  • [...] 我们必须编写满足所有要求的生成器,而不是简单地使用内置的生成器。为什么不确保您使用的生成器生成满足类不变量的实例?此外,您应该考虑完全避开require(正如您所指出的,它会引发异常,因此它不是很实用)并将Foo 的验证和实例化集中在一个返回Try[Foo] 的创建方法中。跨度>

标签: scala scalacheck property-based-testing


【解决方案1】:

更好的方法是完全避免require,而是编写返回Either(或Validation,如果你倾向于scalaz)的智能构造函数。更好的是创建新类型包装器(即case class MyWrapper private (i: Int) extends AnyVal) 用于您的输入类型,其中 那些 包装器带有智能构造函数,因此只能构造有效值。当您为这些定制类型编写生成器时,您可以确保保留所需的不变量。

与其寻找绕过类型系统的方法(通过使用require 在运行时引发异常),不如让类型系统为您工作。这不仅会使编写基于属性的测试变得更加容易,而且整个系统都会受益。

【讨论】:

  • 有趣,Validation 似乎适合我的用例——require 语句不是 super 重要的,简单地记录错误可能就足够了。不过,我主要是尽量避免编写 any 生成器,因为我们有足够的数据类型,这将是一项非常重要的工作。
  • 我很好奇...您能否扩展一下“更好的方法是为您的输入类型创建新类型包装器(即case class MyWrapper private (i: Int) extends AnyVal),其中这些包装器带有智能构造函数,因此只有有效值可以建造。” +1,顺便说一句
【解决方案2】:

不——require是一个非常简单的方法,从source可以看出:

def require(requirement: Boolean) {
  if (!requirement)
    throw new IllegalArgumentException("requirement failed")
}

@inline final def require(requirement: Boolean, message: => Any) {
  if (!requirement)
    throw new IllegalArgumentException("requirement failed: "+ message)
}

我建议提供一种单独的方法来构建您的类,该类只有测试使用。例如,如果您将构造函数的范围限定为包:

package foo

final case class Foo private[foo] (...)

object Foo {
    def apply(...): Foo = /* put assertions in here */
}

然后你可以把你的 scalacheck 生成器放在同一个包中,让它们使用私有构造函数。

或者,如果您真的想对它感到奇怪,您可以编写自己的require 来确定是否根据某些全局状态或隐式参数跳过检查。但这似乎不明智。

【讨论】:

  • 我建议提供一种单独的方法来构建只有测试使用的类。我强烈反对。如果您的测试绕过了应用程序代码,那有什么意义呢?在应用程序代码中使用工厂/创建方法控制实例化,并在生成器中使用该方法是要走的路,IMO。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 2017-01-07
  • 1970-01-01
相关资源
最近更新 更多