diff --git a/akka-docs/src/main/paradox/testing.md b/akka-docs/src/main/paradox/testing.md index 6cbf49e4f0..938a577870 100644 --- a/akka-docs/src/main/paradox/testing.md +++ b/akka-docs/src/main/paradox/testing.md @@ -238,6 +238,27 @@ akka.loggers = [akka.testkit.TestEventListener] @@@ +### Overriding behavior + +Sometimes you want to 'hook into' your actor to be able to test some internals. +Usually it is better to test an actors' external interface, but for example if +you want to test timing-sensitive behavior this can come in handy. Say for +instance you want to test an actor that schedules a task: + +Scala +: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #timer } + +Java +: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #timer } + +You can override the method that does the scheduling in your test: + +Scala +: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #timer-test } + +Java +: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #timer-test } + ### Timing Assertions diff --git a/akka-docs/src/test/java/jdocs/testkit/TestKitDocTest.java b/akka-docs/src/test/java/jdocs/testkit/TestKitDocTest.java index fd32ce4fb3..3c8d2d17cf 100644 --- a/akka-docs/src/test/java/jdocs/testkit/TestKitDocTest.java +++ b/akka-docs/src/test/java/jdocs/testkit/TestKitDocTest.java @@ -21,6 +21,7 @@ import org.junit.Test; import com.typesafe.config.ConfigFactory; +import akka.actor.AbstractActorWithTimers; import akka.actor.ActorKilledException; import akka.actor.ActorRef; import akka.actor.ActorSystem; @@ -29,6 +30,7 @@ import akka.actor.PoisonPill; import akka.actor.Props; import akka.actor.Terminated; import akka.actor.AbstractActor; +import akka.actor.AbstractActor.Receive; import akka.testkit.TestActor.AutoPilot; import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; @@ -37,6 +39,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; public class TestKitDocTest extends AbstractJavaTest { @@ -72,6 +75,29 @@ public class TestKitDocTest extends AbstractJavaTest { } //#test-actor-ref + //#timer + static class TestTimerActor extends AbstractActorWithTimers { + private static Object SCHED_KEY = "SchedKey"; + static final class TriggerScheduling { + } + static final class ScheduledMessage { + } + @Override + public Receive createReceive() { + return receiveBuilder() + .match(TriggerScheduling.class, msg -> triggerScheduling()) + .build(); + } + void triggerScheduling() { + getTimers().startSingleTimer( + SCHED_KEY, + new ScheduledMessage(), + Duration.create(500, TimeUnit.MILLISECONDS) + ); + } + } + //#timer + @Test public void demonstrateAsk() throws Exception { //#test-behavior @@ -258,7 +284,7 @@ public class TestKitDocTest extends AbstractJavaTest { .build(); } } - + // create a test probe final TestKit probe = new TestKit(system); @@ -349,6 +375,23 @@ public class TestKitDocTest extends AbstractJavaTest { //#test-probe-forward } + @Test + public void demonstrateUsingInheritenceToTestTimers() { + //#timer-test + new TestKit(system) {{ + final TestKit probe = new TestKit(system); + final ActorRef target = system.actorOf(Props.create(TestTimerActor.class, () -> new TestTimerActor() { + @Override + void triggerScheduling() { + probe.getRef().tell(new ScheduledMessage(), getSelf()); + } + })); + target.tell(new TestTimerActor.TriggerScheduling(), ActorRef.noSender()); + probe.expectMsgClass(TestTimerActor.ScheduledMessage.class); + }}; + //#timer-test + } + @Test public void demonstrateWithinProbe() { try { diff --git a/akka-docs/src/test/scala/docs/testkit/TestkitDocSpec.scala b/akka-docs/src/test/scala/docs/testkit/TestkitDocSpec.scala index d5f6e610d3..2a843a5bbc 100644 --- a/akka-docs/src/test/scala/docs/testkit/TestkitDocSpec.scala +++ b/akka-docs/src/test/scala/docs/testkit/TestkitDocSpec.scala @@ -69,6 +69,22 @@ object TestKitDocSpec { //#test-probe-forward-actors + //#timer + case class TriggerScheduling(foo: String) + + object SchedKey + case class ScheduledMessage(foo: String) + + class TestTimerActor extends Actor with Timers { + override def receive = { + case TriggerScheduling(foo) ⇒ triggerScheduling(ScheduledMessage(foo)) + } + + def triggerScheduling(msg: ScheduledMessage) = + timers.startSingleTimer(SchedKey, msg, 500.millis) + } + //#timer + class LoggingActor extends Actor { //#logging-receive import akka.event.LoggingReceive @@ -277,6 +293,22 @@ class TestKitDocSpec extends AkkaSpec with DefaultTimeout with ImplicitSender { //#test-probe-forward } + "demonstrate using inheritance to test timers" in { + //#timer-test + import akka.testkit.TestProbe + import akka.actor.Props + + val probe = TestProbe() + val actor = system.actorOf(Props(new TestTimerActor() { + override def triggerScheduling(msg: ScheduledMessage) = + probe.ref ! msg + })) + + actor ! TriggerScheduling("abc") + probe.expectMsg(ScheduledMessage("abc")) + //#timer-test + } + "demonstrate calling thread dispatcher" in { //#calling-thread-dispatcher import akka.testkit.CallingThreadDispatcher