#scala #unit-testing #akka
#scala #модульное тестирование #akka
Вопрос:
Например, у меня есть два актера — родительский актер и дочерний актер. Когда родительский элемент получает сообщение, он порождает столько дочерних актеров, сколько указано в сообщении. Как я могу протестировать эту функциональность? Есть ли способ издеваться над контекстом или каким-либо другим способом проверить, что актеры созданы правильно и в нужном количестве?
class ParentActor extends Actor {
case class CreateChildren(count: Int)
override def receive: Receive = {
case CreateChildren(count) => for (_ <- 0 until count) context.actorOf(Props[ChildActor])
}
}
class ChildActor extends Actor {
override def receive: Receive = {
case _ =>
}
}
Обновлено: решение основано на ответе @Tim
Измененные классы:
class ParentActor(childActorFactory: ChildActorFactory) extends Actor {
override def receive: Receive = {
case CreateChildren(count) => for (_ <- 0 until count) childActorFactory.create(context)
}
}
object ParentActor {
def props(childActorFactory: ChildActorFactory): Props = Props(new ParentActor(childActorFactory))
}
class ChildActorFactory {
def create(context: ActorContext): ActorRef = context.actorOf(Props[ChildActor])
}
Тест:
"ParentActor" should {
"instantiate ten child actors" in {
val childrenCount = 10
val childActorFactory = mock[ChildActorFactory]
val parentActor = TestActorRef[ParentActor](ParentActor.props(childActorFactory))
parentActor ! CreateChildren(childrenCount)
Mockito.verify(childActorFactory, Mockito.times(childrenCount))
.create(parentActor.underlyingActor.context)
}
}
Комментарии:
1. Почему вы хотите создать так много актеров? Разве ты не хочешь поступить
context.become
с одним из них? Вы уверены, что просто хотите их создать?2. @TomerShetah Отдельные участники выполняются асинхронно, что обеспечивает больший параллелизм, в то время как использование
context.become
является последовательным. Это также позволяет локализовать состояние в дочерних субъектах, а не централизовать его в родительском.3. @Tim, насколько я знаю, пока вы не добавите их в контекст, они вообще не вызываются. например, если вы добавите в функцию
ChildActor
приема:case s => println(s)
, и отправите строкуParentActor
, она не будет разрешена.4. @TomerShetah Это псевдокод, предоставленный для поддержки конкретного вопроса о создании и тестировании актеров, поэтому не придавайте слишком большого значения тому факту, что код неполный.
Ответ №1:
Вам нужно издеваться над дочерним актером, если вы хотите полностью протестировать родительского актера, поэтому вы не можете создать дочернего актера непосредственно в родительском актере. Вместо этого передайте фабричный метод родительскому актеру (внедрение зависимостей):
class ParentActor(makeChild: () => ChildActor) extends Actor {
case class CreateChildren(count: Int)
override def receive: Receive = {
case CreateChildren(count) => for (_ <- 0 until count) makeChild()
}
}
makeChild
Функция может подсчитывать количество созданных актеров. Он также может возвращать макет версии ChildActor
, которая реализует тестовое поведение, чтобы подчеркнуть ParentActor
.
Комментарии:
1. Спасибо! Я расширил свой код на основе вашего ответа, и все работает просто отлично.
Ответ №2:
Самый простой способ просто убедиться, что дочерние актеры созданы, — переопределить метод preStart() в ChildActor
class ChildActor extendsActor with ActorLogging {
override def preStart(): Unit = log.info("Child created")
def receive(): Receive = // whatever
}
Таким образом, вы будете видеть сообщение каждый раз, когда дочерний актер добавляется в систему.
Комментарии:
1. Да, но это решение требует ручной проверки журналов. Мне нужен был модульный тест для автоматической проверки, поэтому это решение абсолютно неуместно.