Testing an actors that creates child actors

Viewed 160

For example, I have two actors - a parent actor and a child actor. When a parent receives a message, it spawns as many child actors as specified in the message. How can I test this functionality? Is there a way to mock the context or some other way to check that the actors are created correctly and in the right amount?

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 _ =>
  }
}

Updated: Solution based on @Tim answer

Changed classes:

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])
}

Test:

"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)
    }
  }
2 Answers

You need to mock the child actor if you want to fully test the Parent actor, so you can't create the child actor directly in the parent actor. Instead, pass a factory method to the parent actor (dependency injection):

class ParentActor(makeChild: () => ChildActor) extends Actor {
  case class CreateChildren(count: Int)

  override def receive: Receive = {
    case CreateChildren(count) => for (_ <- 0 until count) makeChild() 
  }
}

The makeChild function can count the number of actors that are created. It can also return a mock version of ChildActor that implements test behaviour to stress the ParentActor.

The simplest way just to make sure that child actors are created, is overriding preStart() method in the ChildActor

class ChildActor extendsActor with ActorLogging {
  override def preStart(): Unit = log.info("Child created")
def receive(): Receive = // whatever
}

So then you'll see the message everytime a child actor is added to the system.

Related