3

我正在尝试使用ScalaTest编写一个基本上声明“它不应该抛出异常,或者抛出可能的异常列表之一”的属性,GeneratorDrivenPropertyChecks而它又使用scalatest。问题是我无法将noException与逻辑或结合起来,所以我能想到的最好的就是这个丑陋的测试:

it should "not throw unexpected exceptions" in {
  forAll { (s: String) =>
    try { parse(s) }
    catch { case e:Throwable => e shouldBe a [ParsingFailedException] }
    true shouldBe true // prevent compile error
}}

相反,我希望看到的内容更像

it should "not throw unexpected exceptions" in {
  forAll { (s: String) => {
    (noException should Be thrownBy) or (a [ParsingFailedException] shouldBe thrownBy) { parse(s)  }
}}
4

1 回答 1

3

由于我们希望将异常用作值而不是控制异常流,因此我们可以使用scala.util.Try的值并对其进行断言Try。所以parse(s)我们可以打电话而不是打电话Try(parse(s))

不幸的是,因为现在已经包装了这些值,所以除了编写自定义匹配器之外,我想不出一种干净的方法来断言谓词。对于您的具体示例,自定义匹配器可能如下所示:

class FailAsMatcher[E <: Throwable](t: Class[E]) extends Matcher[Try[Any]] {
  def apply(theTry: Try[Any]) =
    MatchResult(
      theTry.isFailure && (theTry.failed.get.getClass == t),
      s"did not fail as a $t",
      s"did fail as a $t"
    )
}

由于Try是协变的,我们可以将自定义匹配器的泛型类型定义为Try[Any]。匹配器仅匹配Failure[Any]失败的实例,但提供的类型除外。现在,我们可以将其称为:

it should "not throw unexpected exceptions" in {
  forAll { (s: String) =>
    Try(parse(s)) should ( be ('success) or failAs(classOf[ParsingFailedException]))
  }
}

def failAs[E <: Throwable](t: Class[E]) = new FailAsMatcher[E](t)

现在,如果发生意外异常,错误可能如下所示:

TestFailedException was thrown during property evaluation.
  Message: Failure(java.lang.NullPointerException) was not success, and did not fail as a class ParsingFailedException
于 2014-12-01T23:42:19.530 回答