如何使用 zio-test 测试异常情况

pme*_*pme 4 scala zio zio-test

我有以下功能,我想测试:

def people(id: Int): RIO[R, People]
Run Code Online (Sandbox Code Playgroud)

如果有一个,这个函数会返回 People id,resp。如果不是,则失败,例如:

IO.fail(ServiceException(s"No People with id $id"))
Run Code Online (Sandbox Code Playgroud)

快乐的案例有效,例如:

suite("Get a Person for an ID") (
    testM("get Luke Skywalker") {
      for {
        peopleRef <- Ref.make(Vector(People()))
        luke <- Swapi.>.people(1).provide(Test(peopleRef))
      } yield assert(luke, equalTo(People()))
    },
Run Code Online (Sandbox Code Playgroud)

但是我如何测试失败案例呢?我尝试了不同的东西,主要是类型不匹配。这是一个尝试:

    testM("get not existing People") {
      (for {
        peopleRef <- Ref.make(Vector(People()))
        failure = Swapi.>.people(2).provide(Test(peopleRef))
      } yield assertM(failure, fail(Cause.die(ServiceException(s"No People with id 2")))
    }
  )
Run Code Online (Sandbox Code Playgroud)

Lan*_*fer 8

这是用于assertMZIO 1.0 的另一个紧凑变体:

import zio._
import zio.test.Assertion.{equalTo, fails}
import zio.test._

object ExampleSpec extends DefaultRunnableSpec {
  def spec = suite("ExampleSpec")(
    testM("Example of testing for expected failure") {
      assertM(ZIO.fail("fail").run)(fails(equalTo("fail")))
    }
  )
}
Run Code Online (Sandbox Code Playgroud)

在 ZIO 2.0 中,测试看起来非常相似:

import zio._
import zio.test._
import zio.test.Assertion.{equalTo, fails}

object ExampleSpec extends ZIOSpecDefault {
  def spec = suite("ExampleSpec ZIO 2.0")(
    test("Example of testing for expected failure in ZIO 2.0") {
      assertZIO(ZIO.fail("fail").exit)(fails(equalTo("fail")))
    }
  )
}
Run Code Online (Sandbox Code Playgroud)


Cor*_*ein 8

ZIO 2.0有一些变化:

  • 使用exit而不是run
  • 使用test而不是testM
  • assert有一个新的柯里化语法assert(result)(assertion)
import zio.test._
import zio.test.Assertion._
import zio.ZIO

object ExampleSpec extends DefaultRunnableSpec(
  suite("ExampleSpec")(
    test("Example of testing for expected failure") {
      for {
        result <- ZIO.fail("failureResult").exit
      } yield assert(result)(
          fails(equalTo("failureResult"))
        )
    }
  )
)
Run Code Online (Sandbox Code Playgroud)


Ada*_*ser 7

我想你肯定明白了。我唯一要为其他有类似问题的人补充的是,您的示例还涉及环境类型,这是一个很好的讨论主题,但与如何使用 ZIO Test 测试效果是否按预期失败有关。

我在下面包含了一个如何测试效果是否按预期失败的最小示例。如上所述,您可以调用run效果来获取退出值,然后使用它Assertion.fails来断言效果因已检查异常而失败、因未检查异常Assertion.dies而断言效果失败,或Assertion.interrupted测试效果是否已中断。

另请注意,您不必使用 include equalTo("fail")。如果您只关心效果失败,您可以使用fails(anything). 如果您正在测试某个效果是否因指定的异常而消失,您可以执行类似dies(isSubtype[IllegalArgumentException]).

希望这可以帮助!

import zio.test._
import zio.test.Assertion._
import zio.ZIO

object ExampleSpec
    extends DefaultRunnableSpec(
      suite("ExampleSpec")(
        testM("Example of testing for expected failure") {
          for {
            result <- ZIO.fail("fail")
          } yield assert(result, fails(equalTo("fail")))
        }
      )
    )
Run Code Online (Sandbox Code Playgroud)

  • 根据记录,这个例子对我不起作用。我试图找出测试预期失败的最惯用的方法,您的示例看起来应该有效,但它不适合我编译。错误:(11, 37) 类型不匹配;找到:zio.test.Assertion[zio.Exit[Any,Any]] 必需:zio.test.Assertion[String] } 产量断言(结果,失败(equalTo(“失败”))) (2认同)
  • 看起来我需要附加 `.runs` 才能使用 `fails` 断言,即 `ZIO.fail("fail").run` (2认同)

Moh*_*deh 7

如果您的错误是可抛出的,equalsTo则当它针对正在运行的效果运行时会失败,因此您必须使用isSubtype断言来检查您是否收到正确的错误,这有点棘手:

import zio.test._
import zio.test.Assertion._
import zio.ZIO

object ExampleSpec
    extends DefaultRunnableSpec(
      suite("ExampleSpec")(
        testM("Example of testing for expected failure") {
          for {
            result <- ZIO.fail(new NoSuchElementException).run
          } yield assert(result, fails(isSubtype[NoSuchElementException](anything)))
        }
      )
    )
Run Code Online (Sandbox Code Playgroud)


pme*_*pme 5

ZIO-Chat中@adamfraser 的帮助下:

基本上调用 run 失败的效果,然后断言它是 Assertion.fails 的失败。或者 Assertion.dies 如果它是一个未经检查的异常。

我想我现在有一个很好的解决方案。

testM("get not existing People") {
    for {
      peopleRef <- Ref.make(Vector(People()))
      failure <- Swapi.>.people(2).provide(Test(peopleRef)).run
    } yield assert(
      failure,
      fails(equalTo(ServiceException("No People with id 2")))
    )
  }
Run Code Online (Sandbox Code Playgroud)

仍然欢迎其他解决方案。


Ruu*_*Pul 5

您还可以翻转错误和结果通道:

import zio.test._
import zio.test.Assertion._
import zio.ZIO

object ExampleSpec
    extends DefaultRunnableSpec(
      suite("ExampleSpec")(
        testM("Example of testing for expected failure") {
          for {
            result <- ZIO.fail("fail").flip
          } yield assert(result, equalTo("fail"))
        }
      )
    )
Run Code Online (Sandbox Code Playgroud)