Skip to content

Instantly share code, notes, and snippets.

@archie
Created December 13, 2013 22:42
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save archie/7952671 to your computer and use it in GitHub Desktop.
Save archie/7952671 to your computer and use it in GitHub Desktop.
Examples of testing Akka actor parent-child relationships, mostly based on findings from https://www.assembla.com/spaces/akka/tickets/3043#/activity/ticket:
package pc
import akka.actor.Actor
import akka.actor.Props
import akka.actor.ActorRef
import akka.actor.ActorRefFactory
class Parent extends Actor {
val child = context.actorOf(Props[Child], "child")
var ponged = false
def receive = {
case 'pingit => child ! 'ping
case 'pong => ponged = true
}
}
class Child extends Actor {
def receive = {
case 'ping => context.parent ! 'pong
}
}
class DependentChild(parent: ActorRef) extends Actor {
def receive = {
case 'ping => parent ! 'pong
}
}
class DependentParent(childProps: Props) extends Actor {
val child = context.actorOf(childProps, "child")
var ponged = false
def receive = {
case 'pingit => child ! 'ping
case 'pong => ponged = true
}
}
class GenericDependentParent(childMaker: ActorRefFactory => ActorRef) extends Actor {
val child = childMaker(context)
var ponged = false
def receive = {
case 'pingit => child ! 'ping
case 'pong => ponged = true
}
}
package pc
import org.scalatest.WordSpec
import org.scalatest.Matchers
import akka.testkit.TestKitBase
import akka.actor.ActorSystem
import akka.actor.Props
import akka.actor.Actor
import akka.testkit.ImplicitSender
import akka.testkit.TestProbe
import akka.testkit.TestActorRef
import akka.actor.ActorRefFactory
class MockedChild extends Actor {
def receive = {
case 'ping => sender ! 'pong
}
}
class ParentChildSpec extends WordSpec with Matchers with TestKitBase {
implicit lazy val system = ActorSystem()
"A DependentChild" should {
"be tested without its parent" in {
val probe = TestProbe()
val child = system.actorOf(Props(classOf[DependentChild], probe.ref))
probe.send(child, 'ping)
probe.expectMsg('pong)
}
}
"A DependentParent" should {
"be tested with custom props" in {
val probe = TestProbe()
val parent = TestActorRef(new DependentParent(Props[MockedChild]))
probe.send(parent, 'pingit)
// test some parent state change
parent.underlyingActor.ponged should (be(true) or be(false))
}
}
"A GenericDependentParent" should {
"be tested with a child probe" in {
val probe = TestProbe()
val maker = (_: ActorRefFactory) => probe.ref
val parent = system.actorOf(Props(classOf[GenericDependentParent], maker))
probe.send(parent, 'pingit)
probe.expectMsg('ping)
}
}
"A fabricated parent" should {
"test its child responses" in {
val proxy = TestProbe()
val parent = system.actorOf(Props(new Actor {
val child = context.actorOf(Props[Child], "child")
def receive = {
case x if sender == child => proxy.ref forward x
case x => child forward x
}
}))
proxy.send(parent, 'ping)
proxy.expectMsg('pong)
}
}
}
@flintobrien
Copy link

This was very helpful. Thanks.
For testing that a child is sending to a parent, I also found the following pattern helpful. Passing in parent.ref to TestActorRef was the key.

class ChildActor extends Actor {
  def childishness = context.parent ! "childishness"
}

"Child should send message to parent" in {
   within(1 second) {
      val parent = TestProbe()
      val underTest = TestActorRef[ChildActor]( Props(new ChildActor), parent.ref, "child")
      underTest.underlyingActor.childishness
      parent.expectMsg( "childishness")
   }
}

@dosht
Copy link

dosht commented Mar 3, 2015

Thank you for the example it's really helpful.
In line 37 in the test, I think you should wrap it in within(100 millis)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment