【问题标题】:Unit testing that code runs without exception in ScalaTest对代码在 ScalaTest 中无异常运行的单元测试
【发布时间】:2019-09-27 09:31:16
【问题描述】:

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

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

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 中执行此类测试的正确方法吗?所有的匹配框架似乎都是为了测试结果的值,并捕获预期的异常,但我没有返回结果,我只是想测试未来是否成功结束。

【问题讨论】:

    标签: scala scalatest


    【解决方案1】:

    您可以使用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)
      }
    

    如果你的代码抛出异常,intercept 捕获它,然后测试 成功。 (您希望它抛出异常,它确实发生了。)

    更多详情可以查看herehere

    【讨论】:

    • 这与我的要求相反。如果代码有效,我的测试预计没有异常,所以它不需要拦截一个。基本上测试就是,代码运行无异常,任务成功了吗?
    • 既然你没有在future内返回任何结果,你只需要知道future是在20秒内执行的对吗?
    • 是的,或者如果未来内部执行的内容引发了异常。例如。如果服务器遇到错误并且无法完成任务。
    • 我更新了我的答案,因此如果在任务执行期间出现故障,测试将失败。您仍然需要拦截来捕获耗时超过 20 秒的案例
    • 如果未来超时,那不会过去吗?如果拦截处理了超时异常,那么超时就被视为成功了对吧?
    【解决方案2】:

    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 方法。

    【讨论】:

      猜你喜欢
      • 2019-11-12
      • 2018-01-30
      • 1970-01-01
      • 2019-04-29
      • 2013-08-12
      • 2013-09-22
      • 2011-08-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多