diff --git a/build.sbt b/build.sbt index 2d8c6772881..3ea75495726 100644 --- a/build.sbt +++ b/build.sbt @@ -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 diff --git a/docs/src/main/paradox/routing-dsl/testkit.md b/docs/src/main/paradox/routing-dsl/testkit.md index 9b39bfacbdf..fa022e93b92 100644 --- a/docs/src/main/paradox/routing-dsl/testkit.md +++ b/docs/src/main/paradox/routing-dsl/testkit.md @@ -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: diff --git a/docs/src/test/java/docs/http/javadsl/server/testkit/MyAppWithActor.java b/docs/src/test/java/docs/http/javadsl/server/testkit/MyAppWithActor.java new file mode 100644 index 00000000000..e29e8efaa13 --- /dev/null +++ b/docs/src/test/java/docs/http/javadsl/server/testkit/MyAppWithActor.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009-2020 Lightbend Inc. + */ + +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 replyTo; + public Ping(ActorRef replyTo) { + this.replyTo = replyTo; + } + } + + public Route createRoute(ActorRef actorRef, Scheduler scheduler) { + Duration timeout = Duration.ofSeconds(3); + return + path("ping", () -> + onSuccess(AskPattern.ask(actorRef, Ping::new, timeout, scheduler), result -> + complete(result) + ) + ); + } + +} diff --git a/docs/src/test/java/docs/http/javadsl/server/testkit/TestKitWithActorTest.java b/docs/src/test/java/docs/http/javadsl/server/testkit/TestKitWithActorTest.java new file mode 100644 index 00000000000..aa4254f785e --- /dev/null +++ b/docs/src/test/java/docs/http/javadsl/server/testkit/TestKitWithActorTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009-2020 Lightbend Inc. + */ + +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 system = Adapter.toTyped(system()); + + TestProbe 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 diff --git a/docs/src/test/scala/docs/http/scaladsl/server/TestKitWithActorSpec.scala b/docs/src/test/scala/docs/http/scaladsl/server/TestKitWithActorSpec.scala new file mode 100644 index 00000000000..9e59081f727 --- /dev/null +++ b/docs/src/test/scala/docs/http/scaladsl/server/TestKitWithActorSpec.scala @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 Lightbend Inc. + */ + +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