From f5f36081115c5fb1367474a2adc75bb8c3ad33c6 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 5 Apr 2013 17:24:57 +0200 Subject: [PATCH] Make JavaTestKit match TestKit, see #3042 * Added a few methods * Added (copied and adjusted) JavaDoc --- .../main/java/akka/testkit/JavaTestKit.java | 255 +++++++++++++++++- 1 file changed, 252 insertions(+), 3 deletions(-) diff --git a/akka-testkit/src/main/java/akka/testkit/JavaTestKit.java b/akka-testkit/src/main/java/akka/testkit/JavaTestKit.java index 69e5957eb5..cd45e4f3eb 100644 --- a/akka-testkit/src/main/java/akka/testkit/JavaTestKit.java +++ b/akka-testkit/src/main/java/akka/testkit/JavaTestKit.java @@ -14,8 +14,30 @@ import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; /** - * Java API for the TestProbe. Proper JavaDocs to come once JavaDoccing is - * implemented. + * Java API: Test kit for testing actors. Inheriting from this class enables + * reception of replies from actors, which are queued by an internal actor and + * can be examined using the expectMsg... methods. Assertions and + * bounds concerning timing are available in the form of Within + * blocks. + *

+ * + * Beware of two points: + *

+ * + *

+ * + * */ public class JavaTestKit { private final TestProbe p; @@ -24,8 +46,19 @@ public class JavaTestKit { p = new TestProbe(system); } + /** + * ActorRef of the test actor. Access is provided to enable e.g. registration + * as message target. + */ + public ActorRef getTestActor() { + return p.testActor(); + } + + /** + * Shorthand to get the testActor. + */ public ActorRef getRef() { - return p.ref(); + return getTestActor(); } public ActorSystem getSystem() { @@ -44,6 +77,9 @@ public class JavaTestKit { return d.mul(TestKitExtension.get(p.system()).TestTimeFactor()); } + /** + * Query queue status. + */ public boolean msgAvailable() { return p.msgAvailable(); } @@ -72,14 +108,26 @@ public class JavaTestKit { return p.remainingOr(def); } + /** + * Have the testActor watch someone (i.e. + * getContext().getWatch(...) ). + */ public ActorRef watch(ActorRef ref) { return p.watch(ref); } + /** + * Have the testActor stop watching someone (i.e. + * getContext.unwatch(...)). + */ public ActorRef unwatch(ActorRef ref) { return p.unwatch(ref); } + /** + * Ignore all messages in the test actor for which the given function returns + * true. + */ public abstract class IgnoreMsg { abstract protected boolean ignore(Object msg); @@ -92,14 +140,63 @@ public class JavaTestKit { } } + /** + * Stop ignoring messages in the test actor. + */ public void ignoreNoMsg() { p.ignoreNoMsg(); } + /** + * Install an AutoPilot to drive the testActor: the AutoPilot will be run for + * each received message and can be used to send or forward messages, etc. + * Each invocation must return the AutoPilot for the next round. + */ public void setAutoPilot(TestActor.AutoPilot pilot) { p.setAutoPilot(pilot); } + /** + * Obtain time remaining for execution of the innermost enclosing + * Within block or missing that it returns the properly dilated + * default for this case from settings (key + * "akka.test.single-expect-default"). + */ + public FiniteDuration remaining() { + return p.remaining(); + } + + /** + * Obtain time remaining for execution of the innermost enclosing + * Within block or missing that it returns the given duration. + */ + public FiniteDuration remainingOr(FiniteDuration duration) { + return p.remainingOr(duration); + } + + /** + * Execute code block while bounding its execution time between + * min and max. Within blocks may be + * nested. All methods in this trait which take maximum wait times are + * available in a version which implicitly uses the remaining time governed by + * the innermost enclosing Within block. + *

+ * + * Note that the timeout is scaled using dilated, which uses the + * configuration entry "akka.test.timefactor", while the min Duration is not. + *

+ * + *

+   * 
+   * // the run() method needs to finish within 3 seconds
+   * new Within(duration("3 seconds")) {
+   *   protected void run() {
+   *     // ...
+   *   }
+   * }
+   * 
+   * 
+ */ public abstract class Within { protected abstract void run(); @@ -122,6 +219,18 @@ public class JavaTestKit { } } + /** + * Await until the given condition evaluates to true or the + * timeout expires, whichever comes first. + *

+ * + * If no timeout is given, take it from the innermost enclosing + * Within block. + *

+ * + * Note that the timeout is scaled using Duration.dilated, which uses the + * configuration entry "akka.test.timefactor". + */ public abstract class AwaitCond { protected abstract boolean cond(); @@ -150,6 +259,19 @@ public class JavaTestKit { } } + /** + * Await until the given assert does not throw an exception or the timeout + * expires, whichever comes first. If the timeout expires the last exception + * is thrown. + *

+ * + * If no timeout is given, take it from the innermost enclosing + * Within block. + *

+ * + * Note that the timeout is scaled using Duration.dilated, which uses the + * configuration entry "akka.test.timefactor". + */ public abstract class AwaitAssert { protected abstract void check(); @@ -171,6 +293,30 @@ public class JavaTestKit { } } + /** + * Receive one message from the test actor and assert that the given matching + * function accepts it. Wait time is bounded by the given duration, with an + * AssertionFailure being thrown in case of timeout. + *

+ * The received object as transformed by the matching function can be + * retrieved with the get method. + * + * Use this variant to implement more complicated or conditional processing. + *

+ * + *

+   * 
+   * final String out = new ExpectMsg("match hint") {
+   *   protected String match(Object in) {
+   *     if (in instanceof Integer)
+   *       return "match";
+   *     else
+   *       throw noMatch();
+   *   }
+   * }.get(); // this extracts the received message
+   * 
+   * 
+ */ public abstract class ExpectMsg { private final T result; @@ -198,64 +344,158 @@ public class JavaTestKit { } } + /** + * Same as expectMsgEquals(remaining(), obj), but correctly + * treating the timeFactor. + */ public T expectMsgEquals(T msg) { return p.expectMsg(msg); } + /** + * Receive one message from the test actor and assert that it equals the given + * object. Wait time is bounded by the given duration, with an + * AssertionFailure being thrown in case of timeout. + * + * @return the received object + */ public T expectMsgEquals(FiniteDuration max, T msg) { return p.expectMsg(max, msg); } + /** + * Same as expectMsgClass(remaining(), clazz), but correctly + * treating the timeFactor. + */ public T expectMsgClass(Class clazz) { return p.expectMsgClass(clazz); } + /** + * Receive one message from the test actor and assert that it conforms to the + * given class. Wait time is bounded by the given duration, with an + * AssertionFailure being thrown in case of timeout. + * + * @return the received object + */ public T expectMsgClass(FiniteDuration max, Class clazz) { return p.expectMsgClass(max, clazz); } + /** + * Same as expectMsgAnyOf(remaining(), obj...), but correctly + * treating the timeFactor. + */ public Object expectMsgAnyOf(Object... msgs) { return p.expectMsgAnyOf(Util.immutableSeq(msgs)); } + /** + * Receive one message from the test actor and assert that it equals one of + * the given objects. Wait time is bounded by the given duration, with an + * AssertionFailure being thrown in case of timeout. + * + * @return the received object + */ public Object expectMsgAnyOf(FiniteDuration max, Object... msgs) { return p.expectMsgAnyOf(max, Util.immutableSeq(msgs)); } + /** + * Same as expectMsgAllOf(remaining(), obj...), but correctly + * treating the timeFactor. + */ public Object[] expectMsgAllOf(Object... msgs) { return (Object[]) p.expectMsgAllOf(Util.immutableSeq(msgs)).toArray(Util.classTag(Object.class)); } + /** + * Receive a number of messages from the test actor matching the given number + * of objects and assert that for each given object one is received which + * equals it and vice versa. This construct is useful when the order in which + * the objects are received is not fixed. Wait time is bounded by the given + * duration, with an AssertionFailure being thrown in case of timeout. + */ public Object[] expectMsgAllOf(FiniteDuration max, Object... msgs) { return (Object[]) p.expectMsgAllOf(max, Util.immutableSeq(msgs)).toArray(Util.classTag(Object.class)); } + /** + * Same as expectMsgAnyClassOf(remaining(), obj...), but + * correctly treating the timeFactor. + */ @SuppressWarnings("unchecked") public T expectMsgAnyClassOf(Class... classes) { final Object result = p.expectMsgAnyClassOf(Util.immutableSeq(classes)); return (T) result; } + /** + * Receive one message from the test actor and assert that it conforms to one + * of the given classes. Wait time is bounded by the given duration, with an + * AssertionFailure being thrown in case of timeout. + * + * @return the received object + */ public Object expectMsgAnyClassOf(FiniteDuration max, Class... classes) { return p.expectMsgAnyClassOf(max, Util.immutableSeq(classes)); } + /** + * Same as expectNoMsg(remaining()), but correctly treating the + * timeFactor. + */ public void expectNoMsg() { p.expectNoMsg(); } + /** + * Assert that no message is received for the specified time. + */ public void expectNoMsg(FiniteDuration max) { p.expectNoMsg(max); } + /** + * Same as receiveN(n, remaining()), but correctly treating the + * timeFactor. + */ public Object[] receiveN(int n) { return (Object[]) p.receiveN(n).toArray(Util.classTag(Object.class)); } + /** + * Receive N messages in a row before the given deadline. + */ public Object[] receiveN(int n, FiniteDuration max) { return (Object[]) p.receiveN(n, max).toArray(Util.classTag(Object.class)); } + /** + * Receive one message from the internal queue of the TestActor. If the given + * duration is zero, the queue is polled (non-blocking). + *

+ * + * This method does NOT automatically scale its Duration parameter! + */ + public Object receiveOne(Duration max) { + return p.receiveOne(max); + } + + /** + * Receive a series of messages until one does not match the given + * match function or the idle timeout is met (disabled by + * default) or the overall maximum duration is elapsed. Returns the sequence + * of messages. + *

+ * + * Note that it is not an error to hit the max duration in this + * case. + *

+ * + * One possible use of this method is for testing whether messages of certain + * characteristics are generated at a certain rate. + */ public abstract class ReceiveWhile { abstract protected T match(Object msg) throws Exception; @@ -292,6 +532,15 @@ public class JavaTestKit { } } + /** + * Facilities for selectively filtering out expected events from logging so + * that you can keep your test run’s console output clean and do not miss real + * error messages. + *

+ * + * If the occurrences is set to Integer.MAX_VALUE, + * no tracking is done. + */ public abstract class EventFilter { abstract protected T run();