Как проверить, что актор Akka был создан в Scala

Я пытаюсь написать тест, который проверит, что мой актор ниже создает heartBeatExpireWorker и heartBeatAccepter, но я понятия не имею, как это сделать.

Сначала я подумал, что мог бы использовать Mockhito mock или шпиона вместо контекста, а затем проверить, что я вызвал actOf, но я не могу найти способ внедрить контекст, не нарушая среду тестирования Akka.

Затем я подумал, что мог бы отправить сообщение «Идентифицировать» рабочим, чтобы убедиться, что они существуют. Но мне пришло в голову, что это тоже не сработает, потому что Akka TestKit, похоже, не создает дочерних актеров тестируемого актера. Он может принимать только тестовые зонды, которые могут заменить соседние акторы.

class HeartBeatPumpWorker(chatService: ChatService, target: HeartBeatMessageCmd) extends Actor with ActorLogging with
WorkersReference {

  val heartBeatInterval = chatService.getHeartBeatInterval

  val tick = context.system.scheduler.schedule(0 millis, heartBeatInterval millis, self, SendHeartBeat(target))

  override def postStop() = tick.cancel()

  def receive = {
    case SendHeartBeat(command: HeartBeatMessageCmd) =>
      log.debug("Sending heartbeat")
      //Send heartbeat to GWT
      val userTarget = NetworkWorker.buildEventUserTarget(command.getEventCode, command.getUser)

      val uuid: String = UUID.randomUUID().toString
      val freshCommand = new HeartBeatMessageCmd(command.getUser, command.getEventCode, uuid, command.getUserSession)
      networkWorker ! NetworkBroadcast(userTarget, freshCommand)

      val heartBeatId: String = freshCommand.getUuid
      //create expirer
      val heartBeatExpireWorkerRef = context.actorOf(HeartBeatExpireWorker.props(chatService, freshCommand),
        HeartBeatExpireWorker.name(heartBeatId))
      val heartBeatAccepterRef = context
        .actorOf(HeartBeatAcceptWorker.props(chatService, freshCommand), HeartBeatAcceptWorker.name(heartBeatId))

      //record heartbeat
        chatService.saveSentHeartbeat(heartBeatId, freshCommand.getUserSession, freshCommand.getEventCode,
          freshCommand.getUser,
        freshCommand.getTimeCmdGenerated)
    case _ =>
      log.error("Pumper received unknown message.  This shouldn't happen " + sender.path.toString)
      self ! PoisonPill
  }

}


object HeartBeatPumpWorker {
  def name(eventCode: String, user: String, sessionId: String) = f"HeartBeatPumpWorker-$eventCode-$user-$sessionId"

  def path(eventCode: String, user: String, sessionId: String) : String = {
    EventWorker.Path + "/" + name(eventCode, user, sessionId)
  }

  def props(chatService: ChatService, heartBeatMsgCmd: HeartBeatMessageCmd) = {
    Props(classOf[HeartBeatPumpWorker], chatService, heartBeatMsgCmd)
  }
}

person HappyCoder86    schedule 03.09.2013    source источник


Ответы (2)


Вставьте Props для дочерних элементов (например, HeartBeatAcceptWorker.props) в конструктор родителя HeartBeatPumpWorker. Пройдите любой Props, который вы хотите из теста. Пусть родитель создаст экземпляры дочерних элементов через предоставленный Props. Взаимодействуйте с детьми. Последняя часть зависит от вашего потока данных. Например, если родитель защищает вас от детей, но делегирует им сообщения, отправьте сообщение родителю. Если дети разговаривают друг с другом, используйте тестовые щупы или что-то подобное.

person agilesteel    schedule 04.09.2013
comment
Я не уверен, что следую. Мне нужно, чтобы реквизиты были определены в методе получения, потому что я использую входящий аргумент для определения пути для моих недавно созданных heartBeatExpireWorkerRef и heartBeatAccepterRef. Так что я не думаю, что мне можно вводить реквизит. - person HappyCoder86; 04.09.2013
comment
Ну тогда вам нужно ввести их через сообщение. В крайнем случае вы можете ввести имя ребенка через сообщение, а затем найти его с помощью ActorSelection. Это помогает? - person agilesteel; 04.09.2013
comment
Звучит неплохо, но позволяет ли Akka TestKit создавать дочерних актеров? Все примеры, которые я нашел до сих пор, позволяют актеру взаимодействовать только с тестовыми зондами. Я не нашел примеров тестов, в которых тестируемый актор создает дочерних акторов. Вы знаете, где я могу найти пример такого теста? - person HappyCoder86; 04.09.2013
comment
Я не уверен, что понимаю, что вы имеете в виду. Тестируемый Актер — это просто Актер. Взгляните на главу об отказоустойчивости. , может поможет. - person agilesteel; 04.09.2013

Техника, которую я сейчас использую, заключается в перехвате создания актера и создании TestProbes. В своих актерах я добавляю отдельный трейт ActorMaker:

trait ActorMaker { this: Actor =>
  def makeActor(props: Props) = context.actorOf(props)
}

И используйте его в MyActor extends Actor with ActorMaker вместо context.actorOf.

Для тестов у меня есть TestProbeMaker, который фиксирует всех созданных актеров и их реквизиты:

trait TestProbeMaker { this: Actor =>
  val probes = ListBuffer.empty[(Props, TestProbe)]
  def makeActor(props: Props) = { val probe = TestProbe()
    probes += (props -> probe)
    probe.ref
  }
}

И я смешиваю его во время тестов

val actorUnderTest = TestActorRef(Props(new MyActor with TestProbeMaker))

Таким образом, я могу утверждать точно, какие актеры созданы. Я также могу использовать probe.expectMsg, чтобы подтвердить, что сообщения отправляются этим созданным субъектам.

Для доступа к датчикам используйте actorUnderTest.underlyingActor.asInstanceOf[TestProbeMaker]

person Andrejs    schedule 18.09.2013
comment
Привет, это кажется милым. Но не понятно, как можно получить доступ к TestProbe. В вашем тестовом классе, как вы получаете доступ к списку «зондов», учитывая, что это поле смешано, когда создается ваш тестируемый актер? Я не думаю, что у вас есть пример кода? - person C0deAttack; 11.12.2013
comment
@C0deAttack, вы можете получить список зондов, используя actorUnderTest.underlyingActor, переведя его в TestProbeMaker. - person Andrejs; 12.12.2013