当akka actor在测试线程之外抛出异常时,没有使用scalatest
发布时间:2020-12-16 18:45:50 所属栏目:安全 来源:网络整理
导读:我有一个情况出现并咬我几次我正在测试一个Actor并且Actor意外地抛出一个异常(由于一个bug),但测试仍然通过.现在大多数情况下,Actor中的异常意味着无论测试验证是否都不正确,因此测试失败,但在极少数情况下这不是真的.异常发生在与测试运行器不同的线程中,因
我有一个情况出现并咬我几次我正在测试一个Actor并且Actor意外地抛出一个异常(由于一个bug),但测试仍然通过.现在大多数情况下,Actor中的异常意味着无论测试验证是否都不正确,因此测试失败,但在极少数情况下这不是真的.异常发生在与测试运行器不同的线程中,因此测试运行器对此一无所知.
一个例子是当我使用mock来验证一些依赖项被调用时,由于Actor代码中的错误,我在mock中调用了一个意外的方法.这导致模拟抛出一个异常,炸毁了演员而不是测试.有时这甚至可能导致下游测试因为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并怀疑那里可能会有什么帮助,但我看不到它.我能找到的唯一文件是用它来检查预期的异常,而不是意外的异常. 解决方法
在演员中思考还有另一种解决方案:故障传递给主管,因此这是捕捉它们并将它们输入测试程序的理想场所:
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发送给被测试的演员,并且所有故障 – 预期或不发生 – 都会转到故障探测器进行检查. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |