代码在 ScalaTest 中无异常运行的单元测试

Unit testing that code runs without exception in ScalaTest

TL;DR 我有一个任务 [Unit],在 ScalaTest/FlatSpec 中,测试任务是否在给定时间范围内成功的正确方法是什么?

我有一个服务器-客户端架构的应用程序,客户端和服务器之间的交互是非阻塞的。这是通过调用客户端 returning 一个未来来实现的,该未来在服务器完成工作时完成。重要的是,这个 future 不会 return 返回结果,它只是用来表示服务器已经完成:

val notification: Task[Unit] = myServer.alterState("do this")
Await.result(notification.runAsync, 20.seconds)

我想在这里测试的是服务器是否正确地向客户端发送完成通知。我正在使用 ScalaTest 的 FlatSpec 对此进行测试,在我看来,以下应该是一个有效的测试:

"Server" should "notify client upon completion" in {
    val notification: Task[Unit] = myServer.alterState("do this")
    Await.result(notification.runAsync, 20.seconds)
}

如果服务器回复时间超过 20 秒,Await.result 将抛出异常,测试将捕获该异常并失败。

这是在 flatspec 中执行此类测试的正确方法吗?所有匹配框架似乎都围绕着测试结果的价值,并捕捉预期的异常,但我没有结果 returned,我只是想测试 future 是否成功结束。

您可以使用intercept方法来验证是否抛出异常。

 val notification: Task[Unit] = myServer.alterState("do this")
 notification onComplete {
   case Failure(_) => fail()
   case Success(_) => succeed
 }
 intercept[TimeoutException] { //you can also use Exception instead of TimeoutException
   Await.result(notification.runAsync, 20.seconds)
  }

If your code throws the exception, intercept catches it, and the test succeeds. (You expected it to throw an exception, and it did.)

可以找到更多详细信息here or here

ScalaFutures 启用断言 Future 在指定的时间段内准备就绪,例如

import org.scalatest._
import org.scalatest.concurrent.ScalaFutures
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

class ServerSpec extends FlatSpec with ScalaFutures {
  "Server" should "notify client upon completion" in {
    val notification: Task[Unit] = myServer.alterState("do this")
    assert(notification.runAsync.isReadyWithin(20 seconds))
  }
}

AsyncFlatSpec 允许使用惯用的 Scala 语法,我们可以像这样映射 Future

import org.scalatest._

class ServerSpec extends AsyncFlatSpec {
  "Server" should "notify client upon completion" in {
    val notification: Task[Unit] = myServer.alterState("do this")
    notification.runAsync.map(_ => succeed)
  }
}

但请确保服务器设计为超时,否则测试将挂起。

FlatSpecAwait 可以明确断言 no exception 应该像这样抛出

import org.scalatest._
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

class ServerSpec extends FlatSpec with Matchers {
  "Server" should "notify client upon completion" in {
    val notification: Task[Unit] = myServer.alterState("do this")
    noException should be thrownBy Await.result(notification.runAsync, 20.seconds)
  }
}

就我个人而言,我会推荐 AsyncFlatSpec 方法。