scala - 当 akka Actor 在测试线程之外抛出异常时,scalatest 失败

标签 scala akka scalatest

在我测试 Actor 时,Actor 意外抛出异常(由于错误),但测试仍然通过。现在大多数情况下,Actor 中的异常意味着测试正在验证的任何内容都不会正确输出,因此测试失败,但在极少数情况下并非如此。异常发生在与测试运行程序不同的线程中,因此测试运行程序对此一无所知。

一个例子是,当我使用模拟来验证调用了某些依赖项时,由于 Actor 代码中的错误,我在模拟中调用了一个意外的方法。这会导致模拟抛出一个异常,该异常会炸毁 Actor 但不会炸毁测试。有时这甚至会导致下游测试神秘地失败,因为 Actor 是如何爆炸的。例如:

// using scala 2.10, akka 2.1.1, scalatest 1.9.1, easymock 3.1
// (FunSpec and TestKit)
class SomeAPI {
  def foo(x: String) = println(x)
  def bar(y: String) = println(y)
}

class SomeActor(someApi: SomeAPI) extends Actor {
  def receive = {
    case x:String  =>
      someApi.foo(x)
      someApi.bar(x)
  }
}

describe("problem example") {
  it("calls foo only when it receives a message") {
    val mockAPI = mock[SomeAPI]
    val ref = TestActorRef(new SomeActor(mockAPI))

    expecting {
      mockAPI.foo("Hi").once()
    }

    whenExecuting(mockAPI) {
      ref.tell("Hi", testActor)
    }
  }

  it("ok actor") {
    val ref = TestActorRef(new Actor {
      def receive = {
        case "Hi"  => sender ! "Hello"
      }
    })
    ref.tell("Hi", testActor)
    expectMsg("Hello")
  }
}

“problemExample”通过了,但随后下游“ok actor”由于某种原因失败了,我不太明白……除了这个异常(exception):
cannot reserve actor name '$$b': already terminated
java.lang.IllegalStateException: cannot reserve actor name '$$b': already terminated
at       akka.actor.dungeon.ChildrenContainer$TerminatedChildrenContainer$.reserve(ChildrenContainer.scala:86)
at akka.actor.dungeon.Children$class.reserveChild(Children.scala:78)
at akka.actor.ActorCell.reserveChild(ActorCell.scala:306)
at akka.testkit.TestActorRef.<init>(TestActorRef.scala:29)

因此,我可以通过检查 afterEach 处理程序中的记录器输出来了解捕获此类事件的方法。绝对可行,尽管在我实际期望出现异常的情况下有点复杂,这就是我要测试的内容。但是有没有更直接的方法来处理这个问题并使测试失败?

附录:我查看了 TestEventListener 并怀疑那里可能有帮助,但我看不到它。我能找到的唯一文档是关于使用它来检查预期的异常,而不是意外的异常。

最佳答案

在 Actors 中思考还有另一种解决方案:故障传送到主管,因此这是捕获它们并将它们输入测试程序的理想场所:

val failures = TestProbe()
val props = ... // description for the actor under test
val failureParent = system.actorOf(Props(new Actor {
  val child = context.actorOf(props, "child")
  override val supervisorStrategy = OneForOneStrategy() {
    case f => failures.ref ! f; Stop // or whichever directive is appropriate
  }
  def receive = {
    case msg => child forward msg
  }
}))

您可以通过发送至 failureParent 发送给被测 Actor 。以及所有失败(无论是否预期)转到 failures探头进行检查。

关于scala - 当 akka Actor 在测试线程之外抛出异常时,scalatest 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18619691/

相关文章:

sbt - 如何在 SAME 类中并行运行 ScalaTest 测试

scala - 使用自定义配置在 Debug模式下运行 sbt 项目

scala - 如何在多项目中使用 Play 框架?

scala - 是否可以使用 SBT 为特定依赖项强制执行不同的 scala 版本

scala - 如何从 Akka actor 本身中获取该 actor 的名称?

scala - 如何配置 sbt/ScalaTest 以从依赖项目加载测试配置?

xml - 与多行 XML 案例匹配的模式

scala - 检查actor系统是否完成工作

playframework-2.0 - play 框架 2.1 - 调度异步任务

scalac 在 ScalaTest 测试中发现错误的 forAll 方法