diff --git a/akka-actor-tests/src/test/java/akka/routing/CustomRouteTest.java b/akka-actor-tests/src/test/java/akka/routing/CustomRouteTest.java new file mode 100644 index 0000000000..dc92ace228 --- /dev/null +++ b/akka-actor-tests/src/test/java/akka/routing/CustomRouteTest.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.routing; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.Props; +import akka.routing.RoundRobinRouter; +import akka.testkit.ExtractRoute; + +public class CustomRouteTest { + + static private ActorSystem system; + + // only to test compilability + public void testRoute() { + final ActorRef ref = system.actorOf(new Props().withRouter(new RoundRobinRouter(1))); + final scala.Function1, scala.collection.Iterable> route = ExtractRoute.apply(ref); + route.apply(null); + } + +} diff --git a/akka-actor-tests/src/test/scala/akka/routing/CustomRouteSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/CustomRouteSpec.scala index a031ffcefa..940f76266c 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/CustomRouteSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/CustomRouteSpec.scala @@ -4,33 +4,45 @@ package akka.routing import akka.testkit.AkkaSpec -import akka.actor.Props -import akka.actor.OneForOneStrategy -import akka.actor.SupervisorStrategy -import akka.dispatch.Dispatchers -import akka.testkit.ExtractRoute @org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) class CustomRouteSpec extends AkkaSpec { - class MyRouter extends RouterConfig { + //#custom-router + import akka.actor.{ ActorRef, Props, SupervisorStrategy } + import akka.dispatch.Dispatchers + + class MyRouter(target: ActorRef) extends RouterConfig { override def createRoute(p: Props, prov: RouteeProvider): Route = { prov.createAndRegisterRoutees(p, 1, Nil) { - case (sender, message) ⇒ toAll(sender, prov.routees) + case (sender, message: String) ⇒ Seq(Destination(sender, target)) + case (sender, message) ⇒ toAll(sender, prov.routees) } } override def supervisorStrategy = SupervisorStrategy.defaultStrategy override def routerDispatcher = Dispatchers.DefaultDispatcherId } + //#custom-router "A custom RouterConfig" must { "be testable" in { - val router = system.actorOf(Props.empty.withRouter(new MyRouter)) + //#test-route + import akka.pattern.ask + import akka.testkit.ExtractRoute + import scala.concurrent.Await + import scala.concurrent.util.duration._ + + val target = system.actorOf(Props.empty) + val router = system.actorOf(Props.empty.withRouter(new MyRouter(target))) val route = ExtractRoute(router) - route(testActor -> "hallo").size must be(1) + val r = Await.result(router.ask(CurrentRoutees)(1 second).mapTo[RouterRoutees], 1 second) + r.routees.size must be(1) + route(testActor -> "hallo") must be(Seq(Destination(testActor, target))) + route(testActor -> 12) must be(Seq(Destination(testActor, r.routees.head))) + //#test-route } } diff --git a/akka-docs/scala/testing.rst b/akka-docs/scala/testing.rst index d9183e30b5..9ee9dca425 100644 --- a/akka-docs/scala/testing.rst +++ b/akka-docs/scala/testing.rst @@ -707,3 +707,17 @@ Some `Specs2 `_ users have contributed examples of how to wor * Specifications are by default executed concurrently, which requires some care when writing the tests or alternatively the ``sequential`` keyword. + +Testing Custom Router Logic +=========================== + +Given the following custom (dummy) router: + +.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/CustomRouteSpec.scala#custom-router + +This might be tested by dispatching messages and asserting their reception at +the right destinations, but that can be inconvenient. Therefore exists the +:obj:`ExtractRoute` extractor, which can be used like so: + +.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/CustomRouteSpec.scala#test-route + diff --git a/akka-testkit/src/main/scala/akka/testkit/Routing.scala b/akka-testkit/src/main/scala/akka/testkit/Routing.scala index b32a2eb30a..54fb59b685 100644 --- a/akka-testkit/src/main/scala/akka/testkit/Routing.scala +++ b/akka-testkit/src/main/scala/akka/testkit/Routing.scala @@ -8,6 +8,18 @@ import scala.annotation.tailrec import akka.actor.{ UnstartedCell, ActorRef } import akka.routing.{ RoutedActorRef, RoutedActorCell, Route } +/** + * This object can be used to extract the `Route` out of a RoutedActorRef. + * These are the refs which represent actors created from [[akka.actor.Props]] + * having a [[akka.routing.RouterConfig]]. Use this extractor if you want to + * test the routing directly, i.e. without actually dispatching messages. + * + * {{{ + * val router = system.actorOf(Props[...].withRouter(new MyRouter)) + * val route = ExtractRoute(router) + * route(sender -> message) must be(...) + * }}} + */ object ExtractRoute { def apply(ref: ActorRef): Route = { @tailrec def rec(tries: Int = 10): Route = {