2015-01-11 1 views
3

У меня есть следующая иерархия родителей: родитель -> ребенок -> рабочий , где срок жизни ребенка зависит от желания - когда запрос завершен, дочерний актер должен быть расторгнут. Это я хотел проверить как часть теста. Я создал 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://[email protected]: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://[email protected]: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://[email protected]: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://[email protected]:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232])** 
... 

Я не совсем понимаю, почему последняя ожидаемая проверка Msg здесь не работает, поскольку сообщение отправляется как обычно. Есть ли какая-либо специальная обработка для автоматически полученных сообщений?

Может ли кто-нибудь просить принести какой-то сарай?

Thx

UPDATE: Пытался решить, что, как предложено - удалить оболочку Envelope следующим образом:

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://[email protected]: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://[email protected]: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://[email protected]:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ImagesControllerActor#-1965336139]) 

Это неправильное сообщение от StepParent, но оно каким-то образом извлечено.

+0

Я не уверен, что 'sender' всегда находится в области видимости в тот момент, когда вы используете его в' context.children.foreach (child => child.tell (msg, sender)) '. Вы можете попытаться создать ссылку для него в актере, например: 'case msg: PersistImages => val current_sender = sender; context.children.foreach (child => child.tell (msg, current_sender)) ' – Ashalynd

+0

@Ashalynd Хорошее предложение, но это тоже не помогло. Более того, предыдущее сообщение отправляется точно по тому же пути и успешно. – jaksky

ответ

0

К сожалению, не предлагаемого решения работали, хотя я был уверен, что они могли бы работать и от автоматического приема протоколирование завершенного сообщения было передано единственным способом, которым я получил эту работу, является своего рода «уродливым» решением переведенным завершенным сообщением на что-то еще:

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") 

Не уверен, что происходит, как это кажется, что Отменено тзд не может быть просто пересылаются, даже если он явно получил.

0

StepParent действительно принимает сообщение Envelope сообщения Terminated.

Вы можете увидеть это из журнала:

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) 
} 
+0

Кажется, что что-то подозрительное происходит здесь, потому что удаление только обертывающего объекта. Конверт не помог. – jaksky

0

Ваше утверждение о прекращении не является правильным. В настоящее время он закодирован:

p.expectMsg(4 seconds, Terminated) 

вы в основном говорите, что вы ожидаете сообщение, которое само Terminated типа и не является экземпляром класса Terminated случае.Вы должны изменить утверждение к:

p.expectMsg(4 seconds, Terminated(refToBeTerminated)) 

или еще лучше:

p.expectTerminated(refToBeTerminated, 4 seconds) 

где refToBeTerminated является ActorRef, что вы ждете, чтобы быть прекращено. Я не уверен, что это ваша единственная проблема, но это точно.

EDIT

Конечно, если все, что вы заботитесь о том, что вы получили какой-либо Terminate то у вас есть несколько вариантов, чтобы проверить это. Вы можете попробовать:

p.expectMsgType[Terminated](4 seconds) 

или:

p.expectMsgClass(4 seconds, classOf[Terminated]) 

или даже:

p.expectMsgPF(4 seconds){case t:Terminated => } 
+0

Я надеялся, что таким образом я избегу необходимости в spcify ActorRef для ребенка, которого у меня нет, и это нелегко наблюдать снаружи. Я не особенно заинтересован в соглашении о завершении – jaksky

+0

К сожалению, не тех, кто работал даже тех, кого я видел, они были получены с помощью автоматического получения журнала – jaksky