Skip to content

Commit

Permalink
Add examples of testing actor interactions (#3496)
Browse files Browse the repository at this point in the history
* Add examples of testing actor interactions

* Shorter 'route under test'

* Naming
  • Loading branch information
raboof authored Oct 2, 2020
1 parent 6d4fc77 commit 8043084
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 2 deletions.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ lazy val docs = project("docs")
.addAkkaModuleDependency("akka-actor-typed", "provided", AkkaDependency.docs)
.addAkkaModuleDependency("akka-multi-node-testkit", "provided", AkkaDependency.docs)
.addAkkaModuleDependency("akka-stream-testkit", "provided", AkkaDependency.docs)
.addAkkaModuleDependency("akka-actor-testkit-typed", "provided", AkkaDependency.docs)
.dependsOn(
httpCore, http, httpXml, http2Support, httpMarshallersJava, httpMarshallersScala, httpCaching,
httpTests % "compile;test->test", httpTestkit % "compile;test->test", httpScalafixRules % ScalafixConfig
Expand Down
15 changes: 13 additions & 2 deletions docs/src/main/paradox/routing-dsl/testkit.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,24 @@ The default timeout when testing your routes using the testkit is @scala[1 secon
In order to extend this default timeout, to say 5 seconds, just add the following implicit in scope:

Scala
: @@snip [TestKitFragmentSpec.scala]($test$/scala/docs/http/scaladsl/server/TestKitFragmentSpec.scala) { #timeout-setting }
: @@snip [TestKitFragmentSpec.scala](/docs/src/test/scala/docs/http/scaladsl/server/TestKitFragmentSpec.scala) { #timeout-setting }

Java
: @@snip [WithTimeoutTest.java]($test$/java/docs/http/javadsl/server/testkit/WithTimeoutTest.java) { #timeout-setting }
: @@snip [WithTimeoutTest.java](/docs/src/test/java/docs/http/javadsl/server/testkit/WithTimeoutTest.java) { #timeout-setting }

Remember to configure the timeout using `dilated` if you want to account for slow test systems.

## Testing Actor integration

The @scala[`ScalatestRouteTest`]@java[`JUnitRouteTest`] still provides a Classic @apidoc[akka.actor.ActorSystem],
so if you are not using the Classic API you will need to adapt it:

Scala
: @@snip [TestKitWithActorSpec.scala](/docs/src/test/scala/docs/http/scaladsl/server/TestKitWithActorSpec.scala) { #testkit-actor-integration }

Java
: @@snip [TestKitWithActorTest.java](/docs/src/test/java/docs/http/javadsl/server/testkit/TestKitWithActorTest.java) { #testkit-actor-integration }

## Integration Testing Routes

Use `~!>` to test a route running in full HTTP server mode:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
*/

package docs.http.javadsl.server.testkit;

import akka.actor.typed.ActorRef;
import akka.actor.typed.Scheduler;
import akka.actor.typed.javadsl.AskPattern;
import akka.http.javadsl.server.AllDirectives;
import akka.http.javadsl.server.Route;
import akka.http.scaladsl.model.StatusCodes;
import akka.japi.pf.PFBuilder;

import java.time.Duration;

public class MyAppWithActor extends AllDirectives {
public static class Ping {
public final ActorRef<String> replyTo;
public Ping(ActorRef<String> replyTo) {
this.replyTo = replyTo;
}
}

public Route createRoute(ActorRef<Ping> actorRef, Scheduler scheduler) {
Duration timeout = Duration.ofSeconds(3);
return
path("ping", () ->
onSuccess(AskPattern.ask(actorRef, Ping::new, timeout, scheduler), result ->
complete(result)
)
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
*/

package docs.http.javadsl.server.testkit;

//#testkit-actor-integration
import akka.actor.testkit.typed.javadsl.TestProbe;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.javadsl.Adapter;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.testkit.JUnitRouteTest;

import akka.http.javadsl.testkit.TestRoute;
import akka.http.javadsl.testkit.TestRouteResult;
import org.junit.Test;

public class TestKitWithActorTest extends JUnitRouteTest {

@Test
public void returnPongForGetPing() {
// This test does not use the classic APIs,
// so it needs to adapt the system:
ActorSystem<Void> system = Adapter.toTyped(system());

TestProbe<MyAppWithActor.Ping> probe = TestProbe.create(system);
TestRoute testRoute = testRoute(new MyAppWithActor().createRoute(probe.getRef(), system.scheduler()));

TestRouteResult result = testRoute.run(HttpRequest.GET("/ping"));
MyAppWithActor.Ping ping = probe.expectMessageClass(MyAppWithActor.Ping.class);
ping.replyTo.tell("PONG!");
result.assertEntity("PONG!");
}
}
//#testkit-actor-integration
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2020 Lightbend Inc. <https://www.lightbend.com>
*/

package docs.http.scaladsl.server

//#testkit-actor-integration
import scala.concurrent.duration._
import scala.util.{ Failure, Success }

import akka.actor.testkit.typed.scaladsl.TestProbe
import akka.actor.typed.{ ActorRef, Scheduler }
import akka.actor.typed.scaladsl.AskPattern._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.testkit.ScalatestRouteTest
import akka.util.Timeout

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

object RouteUnderTest {
case class Ping(replyTo: ActorRef[String])

// Your route under test, scheduler is only needed as ask is used
def route(someActor: ActorRef[Ping])(implicit scheduler: Scheduler, timeout: Timeout) = get {
path("ping") {
complete(someActor ? Ping)
}
}
}

class TestKitWithActorSpec extends AnyWordSpec with Matchers with ScalatestRouteTest {
import RouteUnderTest._

// This test does not use the classic APIs,
// so it needs to adapt the system:
import akka.actor.typed.scaladsl.adapter._
implicit val typedSystem = system.toTyped
implicit val timeout = Timeout(500.milliseconds)
implicit val scheduler = system.scheduler

"The service" should {
"return a 'PONG!' response for GET requests to /ping" in {
val probe = TestProbe[Ping]()
val test = Get("/ping") ~> RouteUnderTest.route(probe.ref)
val ping = probe.expectMessageType[Ping]
ping.replyTo ! "PONG!"
test ~> check {
responseAs[String] shouldEqual "PONG!"
}
}
}
}
//#testkit-actor-integration

0 comments on commit 8043084

Please sign in to comment.