[scala断言专栏]--使用断言

英文原文:


http://www.scalatest.org/user_guide/using_assertions



使用断言

ScalaTest在任何风格特征中都默认使用三个断言。您可以使用:

  • assert 一般断言
  • assertResult 将预期与实际值区分开来;
  • assertThrows 以确保一些代码抛出预期的异常。

要在ScalaTest中快速移动,请学习并使用这三个断言。后来如果你喜欢你可以切换到更具表现力的匹配DSL。

ScalaTest的断言定义在特质上Assertions,延伸Suite到所有的风格特征。特色Assertions也提供:

  • assume有条件地取消测试;
  • fail 无条件失败;
  • cancel 无条件取消测试;
  • succeed 无条件地测试成功;
  • intercept 确保一些代码抛出一个预期的异常,然后对该异常进行断言;
  • assertDoesNotCompile 确保一点代码不编译;
  • assertCompiles 确保一些代码确实编译;
  • assertTypeError 确保一些代码不能编译,因为一个类型(不解析)的错误;
  • withClue 添加有关失败的更多信息。

所有这些构造如下所述。

这个assert

在任何Scala程序中,您可以通过调用assert和传递Boolean表达式来编写断言,例如:

val left = 2 
val right = 1
assert(left == right)

如果传递的表达式是trueassert将正常返回。如果false,Scala assert将会突然完成AssertionError。此行为由assert对象中定义的方法提供Predef,其成员隐式导入到每个Scala源文件中。这个Assertions特征定义了另assert一种隐藏该属性的方法Predef。它的行为是一样的,除非false是通过它抛出 TestFailedException而不是AssertionError。为什么?因为不同的是AssertionErrorTestFailedException携带关于堆栈跟踪中的哪个项目的信息表示失败的测试代码行,这可以帮助用户在失败的测试中更快速地找到违规代码行。此外,ScalaTest assert提供比Scala更好的错误消息assert

如果您通过以前的Boolean表达,left == rightassert在ScalaTest试验,失败会报,由于assert目前作为宏来实现,包括报告的左边和右边的值。例如,给出与上述相同的代码,但使用ScalaTest断言:

import org.scalatest.Assertions._
 val left = 2 
val right = 1
assert(left == right)

TestFailedException从此抛出的详细信息assert 将是:“2不等于1”。

ScalaTest的assert宏通过识别传递给的表达式的AST中的模式assert,对于一组有限的常见表达式,给出等效的ScalaTest匹配器表达式给出的错误消息。这里有一些例子,哪里a是1,b是2,c是3,d 是4,xsList(a, b, c)num是1.0:

assert(a == b || c> = d)
 //错误消息:1不等于2,3不大于或等于4

assert(xs.exists(_ == 4))
 //错误消息:列表(1,2,3)不包含4

assert(“hello” .startsWith(“h”)&& “goodbye”.endsWith(“y”))
 //错误消息:“hello”以“h”开头,但“再见”并没有以“y”结尾

assert(num.isInstanceOf [ Int ])
 //错误消息:1.0不是scala.Int的实例

assert(Some2).isEmpty)
 //错误消息:Some(2)不为空

对于无法识别的表达式,宏当前会打印出一个字符串表示形式(已完成)AST并添加"was false"。以下是无法识别的表达式的错误消息的一些示例:

assert(None.isDefined)
 //错误消息:scala.None.isDefined为false

assert(xs.exists(i => i> 10))
 //错误消息:xs.exists(((i:Int)=> i。>(10)))was false

您可以通过提供 String 一个第二个参数来增加标准错误消息 assert ,如下所示:
val attempted = 2
assert(attempted == 1, "Execution was attempted " + left + " times instead of 1 time")

使用此形式 assert ,故障报告将更具体到您的问题域,从而帮助您调试问题。这种 Assertions 特征也混合在一起  TripleEquals ,它为您提供了一个 === 操作员,允许您 Equality 使用数字进行自定义,执行等式检查  Tolerance ,并在编译时使用兄弟特征强制实施类型约束 TypeCheckedTripleEquals

预期结果

尽管assert宏提供了一种自然可读的Scala assert机制的扩展,它提供了良好的错误消息,但由于操作数变长,所以代码变得不太可读。此外,生成的错误消息=====比较不区分实际值和预期值。操作数是刚刚打电话leftright,因为如果一个被命名expected和其他actual,这将是难以让人记住哪个是哪个。为了帮助这些断言的限制,Suite包括一个assertResult可以用来替代的方法assert。要使用assertResult,请将预期值放在括号中assertResult,然后是包含应该导致预期值的代码的花括号。

val a = 5 
val b = 2 
assertResult(2){
  a  -  b
}

在这种情况下,期望值为2,正在测试的代码为a - b。该断言将失败,并且详细信息TestFailedException将显示为“Expected 2, but got 3"。


强迫失败

如果您只需要测试失败,您可以写:

fail("I've got a bad feeling about this")
或者
fail()

取得成功

在异步式的测试,你必须以结束考试的身体Future[Assertion]或 Assertion。ScalaTest的断言(包括匹配器表达式)具有结果类型 Assertion,因此以断言结尾将满足编译器。如果传递给人体试验或函数体Future.map也 不会与类型结束Assertion,但是,您可以通过将固定类型的错误 succeed在测试或函数体的结尾:

succeed // Has type Assertion

预期异常

有时,您需要测试一些方法是否会在某些情况下抛出预期的异常,例如,当无效参数传递给该方法时。您可以在JUnit 3样式中执行此操作,如下所示:

val s = "hi"
try {
  s.charAt(-1)
  fail()
}
catch {
  case _: IndexOutOfBoundsException => // Expected, so continue
}

如果按预期的方式charAt投掷IndexOutOfBoundsException,控制将转移到捕获情况,这不会做任何事情。但是,如果charAt无法抛出异常fail(),则会运行下一条语句。该fail方法总是以a突然完成TestFailedException,从而发出失败的测试信号。

为了使这种常见的情况更容易表达和阅读,ScalaTest提供了两种方法: assertThrowsintercept。以下是您使用的方式assertThrows

val s = “hi” 
assertThrows [ IndexOutOfBoundsException ] { //结果类型:Assertion 
  s.charAt( - 1)
}

此代码的行为与上一个例子非常相似。如果charAt抛出一个实例IndexOutOfBoundsException, assertThrows将返回Succeeded。但是如果charAt正常完成,或者抛出一个异常,assertThrows将会突然出现TestFailedException

intercept方法的行为相同assertThrows,除了不是返回Succeeded, intercept返回捕获的异常,以便您可以进一步检查它。例如,您可能需要确保异常中包含的数据具有预期值。以下是一个例子:

val s = “hi” 
val catch =
  intercept [ IndexOutOfBoundsException ] { //结果类型:IndexOutOfBoundsException 
    s.charAt( - 1)
  }
assert(capture.getMessage.indexOf( - 1 )!=  - 1

检查一段代码是否编译

通常,当创建库时,您可能希望确保代表潜在的“用户错误”的代码的某些安排不会编译,以使您的库具有更高的抗错误性。ScalaTest的Assertions特征包括以下用于此目的的语法:

assertDoesNotCompile(“val a:String = 1”

如果要确保代码段由于类型错误(而不是语法错误)而无法编译,请使用:

assertTypeError(“val a:String = 1”

请注意,assertTypeError如果给定的代码段由于类型错误而无法编译,则调用将成功。一个语法错误仍将导致抛出TestFailedException

如果你想指出的一小段代码编译,你就可以说具有更明显:

assertCompiles(“val a:Int = 1”

虽然以前的三个构造是用编译时确定的,由字符串表示的代码段是否被编译的宏来实现的,但错误在运行时被报告为测试失败。


假设

Trait Assertions还提供允许您取消测试的方法。如果测试所需的资源不可用,您将取消测试。例如,如果测试需要外部数据库联机,而不是,则可以取消测试以指示由于缺少数据库而无法运行该测试。这样的测试假设数据库是可用的,您可以使用该assume方法在测试开始时指示这一点,如下所示:

assume(database.isAvailable)

对于每个重载 assert 方法,trait  Assertions 提供了一个 assume 具有相同签名和行为的重载方法,除了  assume 方法throw, TestCanceledException 而  assert 方法抛出 TestFailedException 。与之一样 assert ,  assume 隐藏Scala方法 Predef ,执行类似的功能,但抛出 AssertionError 。就像你可以一样 assert ,你会收到从传递给AST的宏提取的错误消息 assume ,并且可以可选地提供一个线索字符串来增加这个错误消息。这里有些例子:
assume(database.isAvailable, "The database was down again")
assume(database.getAllUsers.count === 9)

强制取消

对于每个重载fail方法,有cancel一个相同的方法具有相同的签名和行为,除了cancel方法throw, TestCanceledExceptionfail方法抛出 TestFailedException。因此,如果您只需要取消测试,您可以写:

cancel()

如果要使用消息取消测试,只需将消息放在括号中:
cancel("Can't run the test because no internet connection was found")

得到线索

如果您希望通过方法默认提供更多信息,如果此特征,您可以通过多种方式之一提供“线索”字符串。您提供的额外信息(或“线索”)将包含在抛出的异常的详细消息中。双方 assertassertResult提供直接包含线索的方式,intercept其实不然。以下是直接提供的线索示例assert

assert(1 + 1 === 3, "this is a clue")

并在assertResult
assertResult(3, "this is a clue") { 1 + 1 }

前两条语句抛出的异常将包含"this is a clue"异常详细消息中的线索字符串。要获得与失败assertThrows呼叫抛出的异常的详细消息相同的线索,请使用以下命令withClue
withClue("this is a clue") {
  assertThrows[IndexOutOfBoundsException] {
    "hi".charAt(-1)
  }
}

withClue方法只会将线索字符串添加到混合在ModifiableMessage特征中的异常类型的详细消息。有关ModifiableMessage详细信息,请参阅文档。如果您希望在一段代码之后放置一个线索字符串,请参阅文档 AppendedClues




















你可能感兴趣的:(Scala)