Child actor 终止验证
Child actor Termination validation
我有以下 actor 层次结构 parent -> child -> worker
其中 child 的生命范围对请求来说很紧——当请求完成时 child actor 应该被终止。我想将其作为测试的一部分进行验证。我创建 StepParent 作为测试目的,因为我想验证转发给 msg 到 testprobe 的给定请求的响应。
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
我的测试如下:
class ImagesControllerActorTest extends TestKit(ActorSystem("testsystem"))
with WordSpecLike with MustMatchers with StopSystemAfterAll {
val id = "456"
"ControllerActor" must {
"distribute a work to dedicated dedicated workers and combine back results and then terminate" in {
val p = TestProbe()
val ica = system.actorOf(Props(classOf[StepParent], createActorWithMockedWorkers(id, p.ref), "ControllerActor", p.ref), "parent")
p.send(ica, PersistImages(Set(new URL("http://success"), new URL("http://fail"))))
p.expectMsgPF(2 seconds)(validMsgPersistImageActor)
p.expectMsgPF(2 seconds)(validMsgPersistImageActor)
p.expectMsg(2 seconds, ImagesProcessed(id, Set(new URI("file:/"))))
p.expectMsg(4 seconds, Terminated)
}
}
由于最后一次检查预期消息,我的测试失败了:
assertion failed: timeout (4 seconds) during expectMsg while waiting for Terminated
java.lang.AssertionError: assertion failed: timeout (4 seconds) during expectMsg while waiting for Terminated
at scala.Predef$.assert(Predef.scala:179)
at akka.testkit.TestKitBase$class.expectMsg_internal(TestKit.scala:338)
...
根据详细的日志,终止消息也被转发(根据最后一行)
2015-01-11 17:41:10,386 [WARN ] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent/ControllerActor - id: 456 image url: http://fail FAILED
2015-01-11 17:41:10,386 [INFO ] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent/ControllerActor - id: 456 Processing completed with 1 downloded and 1 failed
2015-01-11 17:41:10,387 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe ImagesProcessed(456,Set(file:/))
2015-01-11 17:41:10,392 [DEBUG] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor/$b - stopped
2015-01-11 17:41:10,394 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent/ControllerActor - stopping
2015-01-11 17:41:10,396 [INFO ] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor/$b - Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://testsystem/user/parent/ImagesControllerActor/$b#-426862126] to Actor[akka://testsystem/user/parent/ControllerActor/$b#-426862126] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
2015-01-11 17:41:10,396 [INFO ] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor/$a - Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://testsystem/user/parent/ControllerActor/$a#1067345522] to Actor[akka://testsystem/user/parent/ControllerActor/$a#1067345522] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
2015-01-11 17:41:10,398 [DEBUG] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor - stopped
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/user/parent/ControllerActor/$a - stopped
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232]),Actor[akka://testsystem/user/parent/ControllerActor#-770422232])
**2015-01-11 17:41:10,400 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232])**
...
我真的不明白为什么 last expectedMsg 检查在这里不起作用,因为消息是照常转发的。自动收到的消息是否有任何特殊处理?
有人可以把一些棚子放进去吗?
谢谢
更新:
尝试按照建议解决该问题 - 按如下方式删除包装信封:
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case mssg: Envelope =>
log.debug(s"Envelope msg forwarded to probe $mssg")
probe.tell(mssg.message, sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
2015-01-11 23:52:16,352 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor - stopping
2015-01-11 23:52:16,354 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor - stopped
2015-01-11 23:52:16,358 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe ImagesProcessed(456,Set(file:/))
2015-01-11 23:52:16,358 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139]),Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
2015-01-11 23:52:16,360 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
2015-01-11 23:52:16,365 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/system/testActor2 - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139]),Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
它仍然失败,似乎这里发生了一些可疑的事情:
2015-01-11 23:52:16,360 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ImagesControllerActor#-1965336139])
这不是来自 StepParent 的正确消息,但它以某种方式被提取出来了。
StepParent
实际上收到了 Terminated
消息的 Envelope
。
从日志中可以看出:
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232]),Actor[akka://testsystem/user/parent/ControllerActor#-770422232])
然而,Envelope
消息随后会打印在您的日志中,就好像它是 Terminated
而不是。
该消息是 Envelope
以便包含来自自动发送的消息的发件人元数据,即使在这种情况下,Terminated
消息已经包含发件人信息。
所以为了让你的测试通过,你可以这样做:
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg: Envelope =>
log.debug(s"Envelope msg forwarded to probe $msg")
probe.tell(msg.message, sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
您的终止声明不正确。目前编码为:
p.expectMsg(4 seconds, Terminated)
你基本上是在说你期待的消息是 Terminated
类型本身,而不是 Terminated
案例 class 的实例。您应该将断言更改为:
p.expectMsg(4 seconds, Terminated(refToBeTerminated))
或更好:
p.expectTerminated(refToBeTerminated, 4 seconds)
其中 refToBeTerminated
是您希望终止的 ActorRef
。我不确定这是否是您唯一的问题,但肯定是个问题。
编辑
当然,如果您只关心您是否有任何类型的 Terminate
,那么您有多种选择来测试它。你可以试试:
p.expectMsgType[Terminated](4 seconds)
或:
p.expectMsgClass(4 seconds, classOf[Terminated])
甚至:
p.expectMsgPF(4 seconds){case t:Terminated => }
不幸的是,尽管我也确信它们可以工作,但没有一个提议的解决方案有效,并且从自动接收日志中传递了终止消息,我让这个东西工作的唯一方法是一种 "ugly" 解决方案将终止消息翻译成其他内容:
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg: Terminated =>
log.debug("Parent: Terminated recieved")
probe.tell("controller terminated",sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
并且断言逻辑工作正常:
p.expectMsg(4 seconds,"controller terminated")
不确定发生了什么,因为似乎无法简单地转发已终止的消息,即使它显然已收到。
我有以下 actor 层次结构 parent -> child -> worker 其中 child 的生命范围对请求来说很紧——当请求完成时 child actor 应该被终止。我想将其作为测试的一部分进行验证。我创建 StepParent 作为测试目的,因为我想验证转发给 msg 到 testprobe 的给定请求的响应。
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
我的测试如下:
class ImagesControllerActorTest extends TestKit(ActorSystem("testsystem"))
with WordSpecLike with MustMatchers with StopSystemAfterAll {
val id = "456"
"ControllerActor" must {
"distribute a work to dedicated dedicated workers and combine back results and then terminate" in {
val p = TestProbe()
val ica = system.actorOf(Props(classOf[StepParent], createActorWithMockedWorkers(id, p.ref), "ControllerActor", p.ref), "parent")
p.send(ica, PersistImages(Set(new URL("http://success"), new URL("http://fail"))))
p.expectMsgPF(2 seconds)(validMsgPersistImageActor)
p.expectMsgPF(2 seconds)(validMsgPersistImageActor)
p.expectMsg(2 seconds, ImagesProcessed(id, Set(new URI("file:/"))))
p.expectMsg(4 seconds, Terminated)
}
}
由于最后一次检查预期消息,我的测试失败了:
assertion failed: timeout (4 seconds) during expectMsg while waiting for Terminated
java.lang.AssertionError: assertion failed: timeout (4 seconds) during expectMsg while waiting for Terminated
at scala.Predef$.assert(Predef.scala:179)
at akka.testkit.TestKitBase$class.expectMsg_internal(TestKit.scala:338)
...
根据详细的日志,终止消息也被转发(根据最后一行)
2015-01-11 17:41:10,386 [WARN ] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent/ControllerActor - id: 456 image url: http://fail FAILED
2015-01-11 17:41:10,386 [INFO ] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent/ControllerActor - id: 456 Processing completed with 1 downloded and 1 failed
2015-01-11 17:41:10,387 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe ImagesProcessed(456,Set(file:/))
2015-01-11 17:41:10,392 [DEBUG] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor/$b - stopped
2015-01-11 17:41:10,394 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent/ControllerActor - stopping
2015-01-11 17:41:10,396 [INFO ] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor/$b - Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://testsystem/user/parent/ImagesControllerActor/$b#-426862126] to Actor[akka://testsystem/user/parent/ControllerActor/$b#-426862126] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
2015-01-11 17:41:10,396 [INFO ] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor/$a - Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://testsystem/user/parent/ControllerActor/$a#1067345522] to Actor[akka://testsystem/user/parent/ControllerActor/$a#1067345522] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
2015-01-11 17:41:10,398 [DEBUG] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor - stopped
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/user/parent/ControllerActor/$a - stopped
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232]),Actor[akka://testsystem/user/parent/ControllerActor#-770422232])
**2015-01-11 17:41:10,400 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232])**
...
我真的不明白为什么 last expectedMsg 检查在这里不起作用,因为消息是照常转发的。自动收到的消息是否有任何特殊处理?
有人可以把一些棚子放进去吗?
谢谢
更新: 尝试按照建议解决该问题 - 按如下方式删除包装信封:
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case mssg: Envelope =>
log.debug(s"Envelope msg forwarded to probe $mssg")
probe.tell(mssg.message, sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
2015-01-11 23:52:16,352 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor - stopping
2015-01-11 23:52:16,354 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor - stopped
2015-01-11 23:52:16,358 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe ImagesProcessed(456,Set(file:/))
2015-01-11 23:52:16,358 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139]),Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
2015-01-11 23:52:16,360 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
2015-01-11 23:52:16,365 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/system/testActor2 - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139]),Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
它仍然失败,似乎这里发生了一些可疑的事情:
2015-01-11 23:52:16,360 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ImagesControllerActor#-1965336139])
这不是来自 StepParent 的正确消息,但它以某种方式被提取出来了。
StepParent
实际上收到了 Terminated
消息的 Envelope
。
从日志中可以看出:
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232]),Actor[akka://testsystem/user/parent/ControllerActor#-770422232])
然而,Envelope
消息随后会打印在您的日志中,就好像它是 Terminated
而不是。
该消息是 Envelope
以便包含来自自动发送的消息的发件人元数据,即使在这种情况下,Terminated
消息已经包含发件人信息。
所以为了让你的测试通过,你可以这样做:
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg: Envelope =>
log.debug(s"Envelope msg forwarded to probe $msg")
probe.tell(msg.message, sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
您的终止声明不正确。目前编码为:
p.expectMsg(4 seconds, Terminated)
你基本上是在说你期待的消息是 Terminated
类型本身,而不是 Terminated
案例 class 的实例。您应该将断言更改为:
p.expectMsg(4 seconds, Terminated(refToBeTerminated))
或更好:
p.expectTerminated(refToBeTerminated, 4 seconds)
其中 refToBeTerminated
是您希望终止的 ActorRef
。我不确定这是否是您唯一的问题,但肯定是个问题。
编辑
当然,如果您只关心您是否有任何类型的 Terminate
,那么您有多种选择来测试它。你可以试试:
p.expectMsgType[Terminated](4 seconds)
或:
p.expectMsgClass(4 seconds, classOf[Terminated])
甚至:
p.expectMsgPF(4 seconds){case t:Terminated => }
不幸的是,尽管我也确信它们可以工作,但没有一个提议的解决方案有效,并且从自动接收日志中传递了终止消息,我让这个东西工作的唯一方法是一种 "ugly" 解决方案将终止消息翻译成其他内容:
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg: Terminated =>
log.debug("Parent: Terminated recieved")
probe.tell("controller terminated",sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
并且断言逻辑工作正常:
p.expectMsg(4 seconds,"controller terminated")
不确定发生了什么,因为似乎无法简单地转发已终止的消息,即使它显然已收到。