3.1 使用ZIO测试,写一些简单的程序
解决方案是ZIO Test,它是一个将effect视为第一类值并利用ZIO的全部功能的测试库。
要开始使用ZIO测试,请首先将其添加为依赖项。
libraryDependencies ++= Seq(
"dev.zio" %% "zio-test" % zioVerzion
"dev.zio" %% "zio-test-sbt" % zioVersion,
)
在这里,我们可以通过扩展DefaultRunnableSpec并实现其spec方法。
import zio.test._
import zio.test.Assertion._
object ExampleSpec extends DefaultRunnableSpec {
def spec = suite("ExampleSpec")(test("addition works") {
assert(1 + 1)(equalTo(2))
})
}
到目前为止,这看起来与其他测试框架没有什么不同。每个测试集合都表示为一个spec,可以是一个测试,也可以是包含一个或多个其他spec的suite。
这样,spec就是一棵像树一样的数据结构,可以支持任意级别的suite和test,从而为组织测试提供了极大的灵活性。
我们使用assert操作编写测试,该运算符首先获取一个要声明的值,然后取一个我们希望对该值保留的声明。
在这里,我们使用简单的equalTo断言,该断言只是期望值等于equalTo的参数,但是正如我们将在下一节中看到的那样,我们可以使用各种其他断言来表达更复杂的期望。
让我们看看如何测试ZIO.succeed是否成功获得了我们之前努力的期望值。
object ExampleSpec extends DefaultRunnableSpec {
def spec = suite("ExampleSpec")(
testM("ZIO.succeed succeeds with specified value") {
assertM(ZIO.succeed(1 + 1))(equalTo(2))
}
)
}
不知道你是否发现了其中的不同?
除了用ZIO.succeed(1+1)替换1+1外,我们所做的唯一更改是用testM替换了test并用assertM断言了。
用testM替换test将告诉测试框架该测试将返回ZIO效果。测试框架将自动运行test以及spec中的所有其他测试,并以跨平台一致的方式报告结果。
类似地,用assertM替换assert表示该断言的左侧将是ZIO
effect,并且测试框架应在左侧运行并将其结果与右侧的期望进行比较。assertM在这里并没有什么神奇的。
实际上,我们可以使用map或for理解将assertM替换为assert。
object ExampleSpec extends DefaultRunnableSpec {
def spec = suite("ExampleSpec")(
testM("testing an effect using map operator") {
ZIO.succeed(1 + 1).map(n => assert(n)(equalTo(2)))
},
testM("testing an effect using a for comprehension") {
for {
n <- ZIO.succeed(1 + 1)
} yield assert(n)(equalTo(2))
}
)
}
编写此测试的所有三种方式都是等效的。通常,我们发现在整个测试适合时,单行测试,使用assertM最具可读性.其他的用for推导比较合适。
您可以选择适合自己的样式。您也可以使用&&和||使用逻辑与逻辑或,或使用组合多个assert语句否定断言。
object ExampleSpec extends DefaultRunnableSpec {
def spec = suite("ExampleSpec")(testM("and") {
for {
x <- ZIO.succeed(1) y <- ZIO.succeed(2)
} yield assert(x)(equalTo(1)) && assert(y)(equalTo(2))
})
}