3.2 使用断言
在上面的示例中,我们使用了equalTo断言,这是最基本的断言之一。
仅使用equalTo断言可以使您走得很远,但是在某些情况下,还有许多其他断言会派上用场。
认为Assertion [A]
的是一个非常好的理解断言的方法,该函数传入一个A值并返回一个布尔值,该布尔值表示该值满足断言,否则返回false。
type Assertion[-A] = A => Boolean
def equalTo[A](expected: A): Assertion[A] = actual => actual == expected
这不是确切实现断言的方式,因为通过对值运行断言而返回的数据类型需要包含一些其他信息以支持报告测试结果。
但是,这为您提供一个断言的良好思考模型,类似于我们在上一章中研究的ZIO。zio.test包中的Assertion伴随对象中有各种断言。现在,我们仅提供一些示例来展示其功能。
断言可以专用于特定的数据类型,因此存在各种断言来表达更复杂的逻辑,这些逻辑对于我们而言可能更难直接实现。
例如,当使用集合时,我们可能要断言两个集合具有相同的元素,即使它们的显示顺序不同。
我们可以使用hasSameElements
断言轻松地做到这一点。
object ExampleSpec extends DefaultRunnableSpec {
def spec = suite("ExampleSpec")(test("hasSameElement") {
assert(List(1, 1, 2, 3))(hasSameElements(List(3, 2, 1, 1)))
})
}
失败断言是另一个特别有用的断言,它使我们能够断言一个效果因特定的值而失败。 我们可以通过首先调用效果上的run来获得一个ZIO效果,该效果以表示原始效果结果的Exit值成功,然后使用带有该Exit值的fail断言来使用。
object ExampleSpec extends DefaultRunnableSpec {
def spec = suite("ExampleSpec")(testM("fails") {
for {
exit <- ZIO.effect(1 / 0).catchAll(_ => ZIO.fail(())).run
} yield assert(exit)(fails(isUnit))
})
}
您可能在这里注意到的另一件事是,许多断言将其他断言作为参数。这使您可以表达更具体的断言,即在较大值的一部分上“置零”。
在上面的示例中,失败声明要求ZIO效果的结果为失败,然后允许我们提供另一个参数以对该失败值必须是什么做出更具体的声明。在这种情况下,我们只使用了isUnit断言,它是equalTo(())的简写,但是我们可以使用我们想要的任何断言。
如果您到达某个位置时不关心特定的值,例如,您只关心效果失败,而不关心效果如何失败,则可以使用anything
断言来表示断言结果为true。
关于断言的另一个不错的功能是我们可以使用逻辑合取,析取和否定来组合它们。
例如,假设我们要断言一个整数集合至少具有一个值,并且所有值都大于或等于零。我们可以这样做:
val assertion: Assertion[Iterable[Int]] = isNonEmpty && forall(nonNegative)
// assertion: Assertion[Iterable[Int]] = (isNonEmpty() && forall(isGreaterThanEqualTo(0)))
同样,我们可以换一种表达方式。例如,我们期望一个集合为空或仅包含三个元素。
val assertion: Assertion[Iterable[Any]] = isEmpty || hasSize(equalTo(3))
// assertion: Assertion[Iterable[Any]] = (isEmpty() || hasSize(equalTo(3)))
我们还可以使用not表达否定断言。例如,我们可以表示期望集合至少包含一个重复元素,如下所示:
val assertion: Assertion[Iterable[Any]] = not(isDistinct)
// assertion: Assertion[Iterable[Any]] = not(isDistinct())