我遇到过几次情况,我正在测试一个 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”由于某种原因失败了,我不太明白……除了这个例外:
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 并怀疑那里可能有一些有用的东西,但我看不到它。我能找到的唯一文档是关于使用它来检查预期的异常,而不是意外的异常。