* Unify scala/java versions of the testing docs * Move 'synchronous testing' to the bottom (#22947)
This commit is contained in:
parent
00e7917b9d
commit
037252b2a3
5 changed files with 412 additions and 973 deletions
|
|
@ -102,7 +102,7 @@ There are 3 different types of message dispatchers:
|
||||||
This dispatcher runs invocations on the current thread only. This
|
This dispatcher runs invocations on the current thread only. This
|
||||||
dispatcher does not create any new threads, but it can be used from
|
dispatcher does not create any new threads, but it can be used from
|
||||||
different threads concurrently for the same actor.
|
different threads concurrently for the same actor.
|
||||||
See @ref:[Scala-CallingThreadDispatcher](testing.md#scala-callingthreaddispatcher)
|
See @ref:[CallingThreadDispatcher](testing.md#callingthreaddispatcher)
|
||||||
for details and restrictions.
|
for details and restrictions.
|
||||||
|
|
||||||
* Sharability: Unlimited
|
* Sharability: Unlimited
|
||||||
|
|
|
||||||
|
|
@ -1,681 +0,0 @@
|
||||||
# Testing Actor Systems
|
|
||||||
|
|
||||||
As with any piece of software, automated tests are a very important part of the
|
|
||||||
development cycle. The actor model presents a different view on how units of
|
|
||||||
code are delimited and how they interact, which has an influence on how to
|
|
||||||
perform tests.
|
|
||||||
|
|
||||||
Akka comes with a dedicated module `akka-testkit` for supporting tests at
|
|
||||||
different levels, which fall into two clearly distinct categories:
|
|
||||||
|
|
||||||
* Testing isolated pieces of code without involving the actor model, meaning
|
|
||||||
without multiple threads; this implies completely deterministic behavior
|
|
||||||
concerning the ordering of events and no concurrency concerns and will be
|
|
||||||
called **Unit Testing** in the following.
|
|
||||||
* Testing (multiple) encapsulated actors including multi-threaded scheduling;
|
|
||||||
this implies non-deterministic order of events but shielding from
|
|
||||||
concurrency concerns by the actor model and will be called **Integration
|
|
||||||
Testing** in the following.
|
|
||||||
|
|
||||||
There are of course variations on the granularity of tests in both categories,
|
|
||||||
where unit testing reaches down to white-box tests and integration testing can
|
|
||||||
encompass functional tests of complete actor networks. The important
|
|
||||||
distinction lies in whether concurrency concerns are part of the test or not.
|
|
||||||
The tools offered are described in detail in the following sections.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
Be sure to add the module `akka-testkit` to your dependencies.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
## Synchronous Unit Testing with `TestActorRef`
|
|
||||||
|
|
||||||
Testing the business logic inside `Actor` classes can be divided into
|
|
||||||
two parts: first, each atomic operation must work in isolation, then sequences
|
|
||||||
of incoming events must be processed correctly, even in the presence of some
|
|
||||||
possible variability in the ordering of events. The former is the primary use
|
|
||||||
case for single-threaded unit testing, while the latter can only be verified in
|
|
||||||
integration tests.
|
|
||||||
|
|
||||||
Normally, the `ActorRef` shields the underlying `Actor` instance
|
|
||||||
from the outside, the only communications channel is the actor's mailbox. This
|
|
||||||
restriction is an impediment to unit testing, which led to the inception of the
|
|
||||||
`TestActorRef`. This special type of reference is designed specifically
|
|
||||||
for test purposes and allows access to the actor in two ways: either by
|
|
||||||
obtaining a reference to the underlying actor instance, or by invoking or
|
|
||||||
querying the actor's behaviour (`receive`). Each one warrants its own
|
|
||||||
section below.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
It is highly recommended to stick to traditional behavioural testing (using messaging
|
|
||||||
to ask the Actor to reply with the state you want to run assertions against),
|
|
||||||
instead of using `TestActorRef` whenever possible.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
@@@ warning
|
|
||||||
|
|
||||||
Due to the synchronous nature of `TestActorRef` it will **not** work with some support
|
|
||||||
traits that Akka provides as they require asynchronous behaviours to function properly.
|
|
||||||
Examples of traits that do not mix well with test actor refs are @ref:[PersistentActor](persistence.md#event-sourcing)
|
|
||||||
and @ref:[AtLeastOnceDelivery](persistence.md#at-least-once-delivery) provided by @ref:[Akka Persistence](persistence.md).
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
### Obtaining a Reference to an `Actor`
|
|
||||||
|
|
||||||
Having access to the actual `Actor` object allows application of all
|
|
||||||
traditional unit testing techniques on the contained methods. Obtaining a
|
|
||||||
reference is done like this:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-actor-ref }
|
|
||||||
|
|
||||||
Since `TestActorRef` is generic in the actor type it returns the
|
|
||||||
underlying actor with its proper static type. From this point on you may bring
|
|
||||||
any unit testing tool to bear on your actor as usual.
|
|
||||||
|
|
||||||
### Testing the Actor's Behavior
|
|
||||||
|
|
||||||
When the dispatcher invokes the processing behavior of an actor on a message,
|
|
||||||
it actually calls `apply` on the current behavior registered for the
|
|
||||||
actor. This starts out with the return value of the declared `receive`
|
|
||||||
method, but it may also be changed using `become` and `unbecome` in
|
|
||||||
response to external messages. All of this contributes to the overall actor
|
|
||||||
behavior and it does not lend itself to easy testing on the `Actor`
|
|
||||||
itself. Therefore the `TestActorRef` offers a different mode of
|
|
||||||
operation to complement the `Actor` testing: it supports all operations
|
|
||||||
also valid on normal `ActorRef`. Messages sent to the actor are
|
|
||||||
processed synchronously on the current thread and answers may be sent back as
|
|
||||||
usual. This trick is made possible by the `CallingThreadDispatcher`
|
|
||||||
described below (see [CallingThreadDispatcher](#callingthreaddispatcher)); this dispatcher is set
|
|
||||||
implicitly for any actor instantiated into a `TestActorRef`.
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-behavior }
|
|
||||||
|
|
||||||
As the `TestActorRef` is a subclass of `LocalActorRef` with a few
|
|
||||||
special extras, also aspects like supervision and restarting work properly, but
|
|
||||||
beware that execution is only strictly synchronous as long as all actors
|
|
||||||
involved use the `CallingThreadDispatcher`. As soon as you add elements
|
|
||||||
which include more sophisticated scheduling you leave the realm of unit testing
|
|
||||||
as you then need to think about asynchronicity again (in most cases the problem
|
|
||||||
will be to wait until the desired effect had a chance to happen).
|
|
||||||
|
|
||||||
One more special aspect which is overridden for single-threaded tests is the
|
|
||||||
`receiveTimeout`, as including that would entail asynchronous queuing of
|
|
||||||
`ReceiveTimeout` messages, violating the synchronous contract.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
To summarize: `TestActorRef` overwrites two fields: it sets the
|
|
||||||
dispatcher to `CallingThreadDispatcher.global` and it sets the
|
|
||||||
`receiveTimeout` to None.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
### The Way In-Between: Expecting Exceptions
|
|
||||||
|
|
||||||
If you want to test the actor behavior, including hotswapping, but without
|
|
||||||
involving a dispatcher and without having the `TestActorRef` swallow
|
|
||||||
any thrown exceptions, then there is another mode available for you: just use
|
|
||||||
the `receive` method on `TestActorRef`, which will be forwarded to the
|
|
||||||
underlying actor:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-expecting-exceptions }
|
|
||||||
|
|
||||||
### Use Cases
|
|
||||||
|
|
||||||
You may of course mix and match both modi operandi of `TestActorRef` as
|
|
||||||
suits your test needs:
|
|
||||||
|
|
||||||
* one common use case is setting up the actor into a specific internal state
|
|
||||||
before sending the test message
|
|
||||||
* another is to verify correct internal state transitions after having sent
|
|
||||||
the test message
|
|
||||||
|
|
||||||
Feel free to experiment with the possibilities, and if you find useful
|
|
||||||
patterns, don't hesitate to let the Akka forums know about them! Who knows,
|
|
||||||
common operations might even be worked into nice DSLs.
|
|
||||||
|
|
||||||
<a id="async-integration-testing"></a>
|
|
||||||
## Asynchronous Integration Testing with `TestKit`
|
|
||||||
|
|
||||||
When you are reasonably sure that your actor's business logic is correct, the
|
|
||||||
next step is verifying that it works correctly within its intended environment.
|
|
||||||
The definition of the environment depends of course very much on the problem at
|
|
||||||
hand and the level at which you intend to test, ranging for
|
|
||||||
functional/integration tests to full system tests. The minimal setup consists
|
|
||||||
of the test procedure, which provides the desired stimuli, the actor under
|
|
||||||
test, and an actor receiving replies. Bigger systems replace the actor under
|
|
||||||
test with a network of actors, apply stimuli at varying injection points and
|
|
||||||
arrange results to be sent from different emission points, but the basic
|
|
||||||
principle stays the same in that a single procedure drives the test.
|
|
||||||
|
|
||||||
The `TestKit` class contains a collection of tools which makes this
|
|
||||||
common task easy.
|
|
||||||
|
|
||||||
@@snip [TestKitSampleTest.java]($code$/java/jdocs/testkit/TestKitSampleTest.java) { #fullsample }
|
|
||||||
|
|
||||||
The `TestKit` contains an actor named `testActor` which is the
|
|
||||||
entry point for messages to be examined with the various `expectMsg...`
|
|
||||||
assertions detailed below. The test actor’s reference is obtained using the
|
|
||||||
`getRef()` method as demonstrated above. The `testActor` may also
|
|
||||||
be passed to other actors as usual, usually subscribing it as notification
|
|
||||||
listener. There is a whole set of examination methods, e.g. receiving all
|
|
||||||
consecutive messages matching certain criteria, receiving a whole sequence of
|
|
||||||
fixed messages or classes, receiving nothing for some time, etc.
|
|
||||||
|
|
||||||
The ActorSystem passed in to the constructor of TestKit is accessible via the
|
|
||||||
`getSystem()` method.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
Remember to shut down the actor system after the test is finished (also in
|
|
||||||
case of failure) so that all actors—including the test actor—are stopped.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
### Built-In Assertions
|
|
||||||
|
|
||||||
The above mentioned `expectMsgEquals` is not the only method for
|
|
||||||
formulating assertions concerning received messages, the full set is this:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-expect }
|
|
||||||
|
|
||||||
In these examples, the maximum durations you will find mentioned below are left
|
|
||||||
out, in which case they use the default value from configuration item
|
|
||||||
`akka.test.single-expect-default` which itself defaults to 3 seconds (or they
|
|
||||||
obey the innermost enclosing `Within` as detailed [below](#testkit-within)). The full signatures are:
|
|
||||||
|
|
||||||
*
|
|
||||||
`public <T> T expectMsgEquals(FiniteDuration max, T msg)`
|
|
||||||
The given message object must be received within the specified time; the
|
|
||||||
object will be returned.
|
|
||||||
*
|
|
||||||
`public <T> T expectMsgPF(Duration max, String hint, Function<Object, T> f)`
|
|
||||||
Within the given time period, a message must be received and the given
|
|
||||||
function must be defined for that message; the result from applying
|
|
||||||
the function to the received message is returned.
|
|
||||||
*
|
|
||||||
`public Object expectMsgAnyOf(Duration max, Object... msg)`
|
|
||||||
An object must be received within the given time, and it must be equal
|
|
||||||
(compared with `equals()`) to at least one of the passed reference
|
|
||||||
objects; the received object will be returned.
|
|
||||||
*
|
|
||||||
`public List<Object> expectMsgAllOf(FiniteDuration max, Object... msg)`
|
|
||||||
A number of objects matching the size of the supplied object array must be
|
|
||||||
received within the given time, and for each of the given objects there
|
|
||||||
must exist at least one among the received ones which equals it (compared
|
|
||||||
with `equals()`). The full sequence of received objects is returned in
|
|
||||||
the order received.
|
|
||||||
*
|
|
||||||
`public <T> T expectMsgClass(FiniteDuration max, Class<T> c)`
|
|
||||||
An object which is an instance of the given `Class` must be received
|
|
||||||
within the allotted time frame; the object will be returned. Note that this
|
|
||||||
does a conformance check, if you need the class to be equal you need to
|
|
||||||
verify that afterwards.
|
|
||||||
*
|
|
||||||
`public <T> T expectMsgAnyClassOf(FiniteDuration max, Class<? extends T>... c)`
|
|
||||||
An object must be received within the given time, and it must be an
|
|
||||||
instance of at least one of the supplied `Class` objects; the
|
|
||||||
received object will be returned. Note that this does a conformance check,
|
|
||||||
if you need the class to be equal you need to verify that afterwards.
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
Because of a limitation in Java’s type system it may be necessary to add
|
|
||||||
`@SuppressWarnings("unchecked")` when using this method.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
*
|
|
||||||
`public void expectNoMsg(FiniteDuration max)`
|
|
||||||
No message must be received within the given time. This also fails if a
|
|
||||||
message has been received before calling this method which has not been
|
|
||||||
removed from the queue using one of the other methods.
|
|
||||||
*
|
|
||||||
`List<Object> receiveN(int n, FiniteDuration max)`
|
|
||||||
`n` messages must be received within the given time; the received
|
|
||||||
messages are returned.
|
|
||||||
|
|
||||||
In addition to message reception assertions there are also methods which help
|
|
||||||
with message flows:
|
|
||||||
|
|
||||||
`public <T> List<T> receiveWhile(Duration max, Duration idle, Int messages, Function<Object, T> f)`
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-receivewhile-full }
|
|
||||||
|
|
||||||
Collect messages as long as
|
|
||||||
* they are matching the given function
|
|
||||||
* the given time interval is not used up
|
|
||||||
* the next message is received within the idle timeout
|
|
||||||
* the number of messages has not yet reached the maximum
|
|
||||||
All collected messages are returned.
|
|
||||||
|
|
||||||
`public void awaitCond(Duration max, Duration interval, Supplier<Boolean> p)`
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-awaitCond }
|
|
||||||
|
|
||||||
Poll the given condition every `interval` until it returns `true` or
|
|
||||||
the `max` duration is used up.
|
|
||||||
|
|
||||||
`public void awaitAssert(Duration max, Duration interval, Supplier<Object> a)`
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-awaitAssert }
|
|
||||||
|
|
||||||
Poll the given assert function every `interval` until it does not throw
|
|
||||||
an exception or the `max` duration is used up. If the timeout expires the
|
|
||||||
last exception is thrown.
|
|
||||||
|
|
||||||
There are also cases where not all messages sent to the test kit are actually
|
|
||||||
relevant to the test, but removing them would mean altering the actors under
|
|
||||||
test. For this purpose it is possible to ignore certain messages:
|
|
||||||
|
|
||||||
`public void ignoreMsg(Function<Object, Boolean> f)`
|
|
||||||
`public void ignoreMsg()`
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-ignoreMsg }
|
|
||||||
|
|
||||||
### Expecting Log Messages
|
|
||||||
|
|
||||||
Since an integration test does not allow to the internal processing of the
|
|
||||||
participating actors, verifying expected exceptions cannot be done directly.
|
|
||||||
Instead, use the logging system for this purpose: replacing the normal event
|
|
||||||
handler with the `TestEventListener` and using an `EventFilter`
|
|
||||||
allows assertions on log messages, including those which are generated by
|
|
||||||
exceptions:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-event-filter }
|
|
||||||
|
|
||||||
If a number of occurrences is specific—as demonstrated above—then `intercept()`
|
|
||||||
will block until that number of matching messages have been received or the
|
|
||||||
timeout configured in `akka.test.filter-leeway` is used up (time starts
|
|
||||||
counting after the passed-in block of code returns). In case of a timeout the test
|
|
||||||
fails.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
Be sure to exchange the default logger with the
|
|
||||||
`TestEventListener` in your `application.conf` to enable this
|
|
||||||
function:
|
|
||||||
|
|
||||||
```
|
|
||||||
akka.loggers = [akka.testkit.TestEventListener]
|
|
||||||
```
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
<a id="testkit-within"></a>
|
|
||||||
### Timing Assertions
|
|
||||||
|
|
||||||
Another important part of functional testing concerns timing: certain events
|
|
||||||
must not happen immediately (like a timer), others need to happen before a
|
|
||||||
deadline. Therefore, all examination methods accept an upper time limit within
|
|
||||||
the positive or negative result must be obtained. Lower time limits need to be
|
|
||||||
checked external to the examination, which is facilitated by a new construct
|
|
||||||
for managing time constraints:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-within }
|
|
||||||
|
|
||||||
The block in `within` must complete after a @ref:[Duration](common/duration.md) which
|
|
||||||
is between `min` and `max`, where the former defaults to zero. The
|
|
||||||
deadline calculated by adding the `max` parameter to the block's start
|
|
||||||
time is implicitly available within the block to all examination methods, if
|
|
||||||
you do not specify it, it is inherited from the innermost enclosing
|
|
||||||
`within` block.
|
|
||||||
|
|
||||||
It should be noted that if the last message-receiving assertion of the block is
|
|
||||||
`expectNoMsg` or `receiveWhile`, the final check of the
|
|
||||||
`within` is skipped in order to avoid false positives due to wake-up
|
|
||||||
latencies. This means that while individual contained assertions still use the
|
|
||||||
maximum time bound, the overall block may take arbitrarily longer in this case.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
All times are measured using `System.nanoTime`, meaning that they describe
|
|
||||||
wall time, not CPU time or system time.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
#### Accounting for Slow Test Systems
|
|
||||||
|
|
||||||
The tight timeouts you use during testing on your lightning-fast notebook will
|
|
||||||
invariably lead to spurious test failures on the heavily loaded Jenkins server
|
|
||||||
(or similar). To account for this situation, all maximum durations are
|
|
||||||
internally scaled by a factor taken from the [Configuration](),
|
|
||||||
`akka.test.timefactor`, which defaults to 1.
|
|
||||||
|
|
||||||
You can scale other durations with the same factor by using `dilated` method
|
|
||||||
in `TestKit`.
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #duration-dilation }
|
|
||||||
|
|
||||||
### Using Multiple Probe Actors
|
|
||||||
|
|
||||||
When the actors under test are supposed to send various messages to different
|
|
||||||
destinations, it may be difficult distinguishing the message streams arriving
|
|
||||||
at the `testActor` when using the `TestKit` as shown until now.
|
|
||||||
Another approach is to use it for creation of simple probe actors to be
|
|
||||||
inserted in the message flows. The functionality is best explained using a
|
|
||||||
small example:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe }
|
|
||||||
|
|
||||||
This simple test verifies an equally simple Forwarder actor by injecting a
|
|
||||||
probe as the forwarder’s target. Another example would be two actors A and B
|
|
||||||
which collaborate by A sending messages to B. In order to verify this message
|
|
||||||
flow, a `TestProbe` could be inserted as target of A, using the
|
|
||||||
forwarding capabilities or auto-pilot described below to include a real B in
|
|
||||||
the test setup.
|
|
||||||
|
|
||||||
If you have many test probes, you can name them to get meaningful actor names
|
|
||||||
in test logs and assertions:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe-with-custom-name }
|
|
||||||
|
|
||||||
Probes may also be equipped with custom assertions to make your test code even
|
|
||||||
more concise and clear:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-special-probe }
|
|
||||||
|
|
||||||
You have complete flexibility here in mixing and matching the
|
|
||||||
`TestKit` facilities with your own checks and choosing an intuitive
|
|
||||||
name for it. In real life your code will probably be a bit more complicated
|
|
||||||
than the example given above; just use the power!
|
|
||||||
|
|
||||||
@@@ warning
|
|
||||||
|
|
||||||
Any message send from a `TestProbe` to another actor which runs on the
|
|
||||||
CallingThreadDispatcher runs the risk of dead-lock, if that other actor might
|
|
||||||
also send to this probe. The implementation of `TestProbe.watch` and
|
|
||||||
`TestProbe.unwatch` will also send a message to the watchee, which
|
|
||||||
means that it is dangerous to try watching e.g. `TestActorRef` from a
|
|
||||||
`TestProbe`.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
#### Watching Other Actors from Probes
|
|
||||||
|
|
||||||
A `TestKit` can register itself for DeathWatch of any other actor:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe-watch }
|
|
||||||
|
|
||||||
#### Replying to Messages Received by Probes
|
|
||||||
|
|
||||||
The probe stores the sender of the last dequeued message (i.e. after its
|
|
||||||
`expectMsg*` reception), which may be retrieved using the
|
|
||||||
`getLastSender()` method. This information can also implicitly be used
|
|
||||||
for having the probe reply to the last received message:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe-reply }
|
|
||||||
|
|
||||||
#### Forwarding Messages Received by Probes
|
|
||||||
|
|
||||||
The probe can also forward a received message (i.e. after its `expectMsg*`
|
|
||||||
reception), retaining the original sender:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe-forward }
|
|
||||||
|
|
||||||
#### Auto-Pilot
|
|
||||||
|
|
||||||
Receiving messages in a queue for later inspection is nice, but in order to
|
|
||||||
keep a test running and verify traces later you can also install an
|
|
||||||
`AutoPilot` in the participating test probes (actually in any
|
|
||||||
`TestKit`) which is invoked before enqueueing to the inspection queue.
|
|
||||||
This code can be used to forward messages, e.g. in a chain `A --> Probe -->
|
|
||||||
B`, as long as a certain protocol is obeyed.
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-auto-pilot }
|
|
||||||
|
|
||||||
The `run` method must return the auto-pilot for the next message, wrapped
|
|
||||||
in an `Option`; setting it to `None` terminates the auto-pilot.
|
|
||||||
|
|
||||||
#### Caution about Timing Assertions
|
|
||||||
|
|
||||||
The behavior of `within` blocks when using test probes might be perceived
|
|
||||||
as counter-intuitive: you need to remember that the nicely scoped deadline as
|
|
||||||
described [above](#testkit-within) is local to each probe. Hence, probes
|
|
||||||
do not react to each other's deadlines or to the deadline set in an enclosing
|
|
||||||
`TestKit` instance:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-within-probe }
|
|
||||||
|
|
||||||
Here, the `expectMsgEquals` call will use the default timeout.
|
|
||||||
|
|
||||||
### Testing parent-child relationships
|
|
||||||
|
|
||||||
The parent of an actor is always the actor that created it. At times this leads to
|
|
||||||
a coupling between the two that may not be straightforward to test.
|
|
||||||
There are several approaches to improve testability of a child actor that
|
|
||||||
needs to refer to its parent:
|
|
||||||
|
|
||||||
1. when creating a child, pass an explicit reference to its parent
|
|
||||||
2. create the child with a `TestProbe` as parent
|
|
||||||
3. create a fabricated parent when testing
|
|
||||||
|
|
||||||
Conversely, a parent's binding to its child can be lessened as follows:
|
|
||||||
|
|
||||||
4. when creating a parent, tell the parent how to create its child
|
|
||||||
|
|
||||||
For example, the structure of the code you want to test may follow this pattern:
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-example }
|
|
||||||
|
|
||||||
#### Introduce child to its parent
|
|
||||||
|
|
||||||
The first option is to avoid use of the `context.parent` function and create
|
|
||||||
a child with a custom parent by passing an explicit reference to its parent instead.
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-dependentchild }
|
|
||||||
|
|
||||||
#### Create the child using TestKit
|
|
||||||
|
|
||||||
The `TestKit` class can in fact create actors that will run with the test probe as parent.
|
|
||||||
This will cause any messages the child actor sends to *getContext().getParent()* to
|
|
||||||
end up in the test probe.
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-TestProbe-parent }
|
|
||||||
|
|
||||||
#### Using a fabricated parent
|
|
||||||
|
|
||||||
If you prefer to avoid modifying the child constructor you can
|
|
||||||
create a fabricated parent in your test. This, however, does not enable you to test
|
|
||||||
the parent actor in isolation.
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-fabricated-parent-creator }
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-fabricated-parent }
|
|
||||||
|
|
||||||
#### Externalize child making from the parent
|
|
||||||
|
|
||||||
Alternatively, you can tell the parent how to create its child. There are two ways
|
|
||||||
to do this: by giving it a `Props` object or by giving it a function which takes care of creating the child actor:
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-dependentparent }
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-dependentparent-generic }
|
|
||||||
|
|
||||||
Creating the `Actor` is straightforward and the function may look like this in your test code:
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #child-maker-test }
|
|
||||||
|
|
||||||
And like this in your application code:
|
|
||||||
|
|
||||||
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #child-maker-prod }
|
|
||||||
|
|
||||||
Which of these methods is the best depends on what is most important to test. The
|
|
||||||
most generic option is to create the parent actor by passing it a function that is
|
|
||||||
responsible for the Actor creation, but using TestProbe or having a fabricated parent is often sufficient.
|
|
||||||
|
|
||||||
<a id="java-callingthreaddispatcher"></a>
|
|
||||||
## CallingThreadDispatcher
|
|
||||||
|
|
||||||
The `CallingThreadDispatcher` serves good purposes in unit testing, as
|
|
||||||
described above, but originally it was conceived in order to allow contiguous
|
|
||||||
stack traces to be generated in case of an error. As this special dispatcher
|
|
||||||
runs everything which would normally be queued directly on the current thread,
|
|
||||||
the full history of a message's processing chain is recorded on the call stack,
|
|
||||||
so long as all intervening actors run on this dispatcher.
|
|
||||||
|
|
||||||
### How to use it
|
|
||||||
|
|
||||||
Just set the dispatcher as you normally would:
|
|
||||||
|
|
||||||
@@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #calling-thread-dispatcher }
|
|
||||||
|
|
||||||
### How it works
|
|
||||||
|
|
||||||
When receiving an invocation, the `CallingThreadDispatcher` checks
|
|
||||||
whether the receiving actor is already active on the current thread. The
|
|
||||||
simplest example for this situation is an actor which sends a message to
|
|
||||||
itself. In this case, processing cannot continue immediately as that would
|
|
||||||
violate the actor model, so the invocation is queued and will be processed when
|
|
||||||
the active invocation on that actor finishes its processing; thus, it will be
|
|
||||||
processed on the calling thread, but simply after the actor finishes its
|
|
||||||
previous work. In the other case, the invocation is simply processed
|
|
||||||
immediately on the current thread. Futures scheduled via this dispatcher are
|
|
||||||
also executed immediately.
|
|
||||||
|
|
||||||
This scheme makes the `CallingThreadDispatcher` work like a general
|
|
||||||
purpose dispatcher for any actors which never block on external events.
|
|
||||||
|
|
||||||
In the presence of multiple threads it may happen that two invocations of an
|
|
||||||
actor running on this dispatcher happen on two different threads at the same
|
|
||||||
time. In this case, both will be processed directly on their respective
|
|
||||||
threads, where both compete for the actor's lock and the loser has to wait.
|
|
||||||
Thus, the actor model is left intact, but the price is loss of concurrency due
|
|
||||||
to limited scheduling. In a sense this is equivalent to traditional mutex style
|
|
||||||
concurrency.
|
|
||||||
|
|
||||||
The other remaining difficulty is correct handling of suspend and resume: when
|
|
||||||
an actor is suspended, subsequent invocations will be queued in thread-local
|
|
||||||
queues (the same ones used for queuing in the normal case). The call to
|
|
||||||
`resume`, however, is done by one specific thread, and all other threads
|
|
||||||
in the system will probably not be executing this specific actor, which leads
|
|
||||||
to the problem that the thread-local queues cannot be emptied by their native
|
|
||||||
threads. Hence, the thread calling `resume` will collect all currently
|
|
||||||
queued invocations from all threads into its own queue and process them.
|
|
||||||
|
|
||||||
### Limitations
|
|
||||||
|
|
||||||
@@@ warning
|
|
||||||
|
|
||||||
In case the CallingThreadDispatcher is used for top-level actors, but
|
|
||||||
without going through TestActorRef, then there is a time window during which
|
|
||||||
the actor is awaiting construction by the user guardian actor. Sending
|
|
||||||
messages to the actor during this time period will result in them being
|
|
||||||
enqueued and then executed on the guardian’s thread instead of the caller’s
|
|
||||||
thread. To avoid this, use TestActorRef.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
If an actor's behavior blocks on a something which would normally be affected
|
|
||||||
by the calling actor after having sent the message, this will obviously
|
|
||||||
dead-lock when using this dispatcher. This is a common scenario in actor tests
|
|
||||||
based on `CountDownLatch` for synchronization:
|
|
||||||
|
|
||||||
```scala
|
|
||||||
val latch = new CountDownLatch(1)
|
|
||||||
actor ! startWorkAfter(latch) // actor will call latch.await() before proceeding
|
|
||||||
doSomeSetupStuff()
|
|
||||||
latch.countDown()
|
|
||||||
```
|
|
||||||
|
|
||||||
The example would hang indefinitely within the message processing initiated on
|
|
||||||
the second line and never reach the fourth line, which would unblock it on a
|
|
||||||
normal dispatcher.
|
|
||||||
|
|
||||||
Thus, keep in mind that the `CallingThreadDispatcher` is not a
|
|
||||||
general-purpose replacement for the normal dispatchers. On the other hand it
|
|
||||||
may be quite useful to run your actor network on it for testing, because if it
|
|
||||||
runs without dead-locking chances are very high that it will not dead-lock in
|
|
||||||
production.
|
|
||||||
|
|
||||||
@@@ warning
|
|
||||||
|
|
||||||
The above sentence is unfortunately not a strong guarantee, because your
|
|
||||||
code might directly or indirectly change its behavior when running on a
|
|
||||||
different dispatcher. If you are looking for a tool to help you debug
|
|
||||||
dead-locks, the `CallingThreadDispatcher` may help with certain error
|
|
||||||
scenarios, but keep in mind that it has may give false negatives as well as
|
|
||||||
false positives.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
### Thread Interruptions
|
|
||||||
|
|
||||||
If the CallingThreadDispatcher sees that the current thread has its
|
|
||||||
`isInterrupted()` flag set when message processing returns, it will throw an
|
|
||||||
`InterruptedException` after finishing all its processing (i.e. all
|
|
||||||
messages which need processing as described above are processed before this
|
|
||||||
happens). As `tell` cannot throw exceptions due to its contract, this
|
|
||||||
exception will then be caught and logged, and the thread’s interrupted status
|
|
||||||
will be set again.
|
|
||||||
|
|
||||||
If during message processing an `InterruptedException` is thrown then it
|
|
||||||
will be caught inside the CallingThreadDispatcher’s message handling loop, the
|
|
||||||
thread’s interrupted flag will be set and processing continues normally.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
The summary of these two paragraphs is that if the current thread is
|
|
||||||
interrupted while doing work under the CallingThreadDispatcher, then that
|
|
||||||
will result in the `isInterrupted` flag to be `true` when the message
|
|
||||||
send returns and no `InterruptedException` will be thrown.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
### Benefits
|
|
||||||
|
|
||||||
To summarize, these are the features with the `CallingThreadDispatcher`
|
|
||||||
has to offer:
|
|
||||||
|
|
||||||
* Deterministic execution of single-threaded tests while retaining nearly full
|
|
||||||
actor semantics
|
|
||||||
* Full message processing history leading up to the point of failure in
|
|
||||||
exception stack traces
|
|
||||||
* Exclusion of certain classes of dead-lock scenarios
|
|
||||||
|
|
||||||
<a id="actor-logging"></a>
|
|
||||||
## Tracing Actor Invocations
|
|
||||||
|
|
||||||
The testing facilities described up to this point were aiming at formulating
|
|
||||||
assertions about a system’s behavior. If a test fails, it is usually your job
|
|
||||||
to find the cause, fix it and verify the test again. This process is supported
|
|
||||||
by debuggers as well as logging, where the Akka toolkit offers the following
|
|
||||||
options:
|
|
||||||
|
|
||||||
*
|
|
||||||
*Logging of exceptions thrown within Actor instances*
|
|
||||||
This is always on; in contrast to the other logging mechanisms, this logs at
|
|
||||||
`ERROR` level.
|
|
||||||
*
|
|
||||||
*Logging of special messages*
|
|
||||||
Actors handle certain special messages automatically, e.g. `Kill`,
|
|
||||||
`PoisonPill`, etc. Tracing of these message invocations is enabled by
|
|
||||||
the setting `akka.actor.debug.autoreceive`, which enables this on all
|
|
||||||
actors.
|
|
||||||
*
|
|
||||||
*Logging of the actor lifecycle*
|
|
||||||
Actor creation, start, restart, monitor start, monitor stop and stop may be traced by
|
|
||||||
enabling the setting `akka.actor.debug.lifecycle`; this, too, is enabled
|
|
||||||
uniformly on all actors.
|
|
||||||
|
|
||||||
All these messages are logged at `DEBUG` level. To summarize, you can enable
|
|
||||||
full logging of actor activities using this configuration fragment:
|
|
||||||
|
|
||||||
```
|
|
||||||
akka {
|
|
||||||
loglevel = "DEBUG"
|
|
||||||
actor {
|
|
||||||
debug {
|
|
||||||
autoreceive = on
|
|
||||||
lifecycle = on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
There are several configuration properties for the TestKit module, please refer
|
|
||||||
to the @ref:[reference configuration](general/configuration.md#config-akka-testkit).
|
|
||||||
1
akka-docs/src/main/paradox/java/testing.md
Symbolic link
1
akka-docs/src/main/paradox/java/testing.md
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../scala/testing.md
|
||||||
|
|
@ -102,7 +102,7 @@ There are 3 different types of message dispatchers:
|
||||||
This dispatcher runs invocations on the current thread only. This
|
This dispatcher runs invocations on the current thread only. This
|
||||||
dispatcher does not create any new threads, but it can be used from
|
dispatcher does not create any new threads, but it can be used from
|
||||||
different threads concurrently for the same actor.
|
different threads concurrently for the same actor.
|
||||||
See @ref:[Scala-CallingThreadDispatcher](testing.md#scala-callingthreaddispatcher)
|
See @ref:[CallingThreadDispatcher](testing.md#callingthreaddispatcher)
|
||||||
for details and restrictions.
|
for details and restrictions.
|
||||||
|
|
||||||
* Sharability: Unlimited
|
* Sharability: Unlimited
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,11 @@
|
||||||
# Testing Actor Systems
|
# Testing Actor Systems
|
||||||
|
|
||||||
@@toc
|
|
||||||
|
|
||||||
@@@ index
|
|
||||||
|
|
||||||
* [testkit-example](testkit-example.md)
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
As with any piece of software, automated tests are a very important part of the
|
As with any piece of software, automated tests are a very important part of the
|
||||||
development cycle. The actor model presents a different view on how units of
|
development cycle. The actor model presents a different view on how units of
|
||||||
code are delimited and how they interact, which has an influence on how to
|
code are delimited and how they interact, which has an influence on how to
|
||||||
perform tests.
|
perform tests.
|
||||||
|
|
||||||
Akka comes with a dedicated module `akka-testkit` for supporting tests at
|
Akka comes with a dedicated module `akka-testkit` for supporting tests.
|
||||||
different levels, which fall into two clearly distinct categories:
|
|
||||||
|
|
||||||
* Testing isolated pieces of code without involving the actor model, meaning
|
|
||||||
without multiple threads; this implies completely deterministic behavior
|
|
||||||
concerning the ordering of events and no concurrency concerns and will be
|
|
||||||
called **Unit Testing** in the following.
|
|
||||||
* Testing (multiple) encapsulated actors including multi-threaded scheduling;
|
|
||||||
this implies non-deterministic order of events but shielding from
|
|
||||||
concurrency concerns by the actor model and will be called **Integration
|
|
||||||
Testing** in the following.
|
|
||||||
|
|
||||||
There are of course variations on the granularity of tests in both categories,
|
|
||||||
where unit testing reaches down to white-box tests and integration testing can
|
|
||||||
encompass functional tests of complete actor networks. The important
|
|
||||||
distinction lies in whether concurrency concerns are part of the test or not.
|
|
||||||
The tools offered are described in detail in the following sections.
|
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
||||||
|
|
@ -37,140 +13,12 @@ Be sure to add the module `akka-testkit` to your dependencies.
|
||||||
|
|
||||||
@@@
|
@@@
|
||||||
|
|
||||||
## Synchronous Unit Testing with `TestActorRef`
|
## Asynchronous Testing: `TestKit`
|
||||||
|
|
||||||
Testing the business logic inside `Actor` classes can be divided into
|
|
||||||
two parts: first, each atomic operation must work in isolation, then sequences
|
|
||||||
of incoming events must be processed correctly, even in the presence of some
|
|
||||||
possible variability in the ordering of events. The former is the primary use
|
|
||||||
case for single-threaded unit testing, while the latter can only be verified in
|
|
||||||
integration tests.
|
|
||||||
|
|
||||||
Normally, the `ActorRef` shields the underlying `Actor` instance
|
|
||||||
from the outside, the only communications channel is the actor's mailbox. This
|
|
||||||
restriction is an impediment to unit testing, which led to the inception of the
|
|
||||||
`TestActorRef`. This special type of reference is designed specifically
|
|
||||||
for test purposes and allows access to the actor in two ways: either by
|
|
||||||
obtaining a reference to the underlying actor instance, or by invoking or
|
|
||||||
querying the actor's behaviour (`receive`). Each one warrants its own
|
|
||||||
section below.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
It is highly recommended to stick to traditional behavioural testing (using messaging
|
|
||||||
to ask the Actor to reply with the state you want to run assertions against),
|
|
||||||
instead of using `TestActorRef` whenever possible.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
@@@ warning
|
|
||||||
|
|
||||||
Due to the synchronous nature of `TestActorRef` it will **not** work with some support
|
|
||||||
traits that Akka provides as they require asynchronous behaviours to function properly.
|
|
||||||
Examples of traits that do not mix well with test actor refs are @ref:[PersistentActor](persistence.md#event-sourcing)
|
|
||||||
and @ref:[AtLeastOnceDelivery](persistence.md#at-least-once-delivery) provided by @ref:[Akka Persistence](persistence.md).
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
### Obtaining a Reference to an `Actor`
|
|
||||||
|
|
||||||
Having access to the actual `Actor` object allows application of all
|
|
||||||
traditional unit testing techniques on the contained methods. Obtaining a
|
|
||||||
reference is done like this:
|
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-actor-ref }
|
|
||||||
|
|
||||||
Since `TestActorRef` is generic in the actor type it returns the
|
|
||||||
underlying actor with its proper static type. From this point on you may bring
|
|
||||||
any unit testing tool to bear on your actor as usual.
|
|
||||||
|
|
||||||
<a id="testfsmref"></a>
|
|
||||||
### Testing Finite State Machines
|
|
||||||
|
|
||||||
If your actor under test is a `FSM`, you may use the special
|
|
||||||
`TestFSMRef` which offers all features of a normal `TestActorRef`
|
|
||||||
and in addition allows access to the internal state:
|
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-fsm-ref }
|
|
||||||
|
|
||||||
Due to a limitation in Scala’s type inference, there is only the factory method
|
|
||||||
shown above, so you will probably write code like `TestFSMRef(new MyFSM)`
|
|
||||||
instead of the hypothetical `ActorRef`-inspired `TestFSMRef[MyFSM]`.
|
|
||||||
All methods shown above directly access the FSM state without any
|
|
||||||
synchronization; this is perfectly alright if the `CallingThreadDispatcher`
|
|
||||||
is used and no other threads are involved, but it may lead to surprises if you
|
|
||||||
were to actually exercise timer events, because those are executed on the
|
|
||||||
`Scheduler` thread.
|
|
||||||
|
|
||||||
### Testing the Actor's Behavior
|
|
||||||
|
|
||||||
When the dispatcher invokes the processing behavior of an actor on a message,
|
|
||||||
it actually calls `apply` on the current behavior registered for the
|
|
||||||
actor. This starts out with the return value of the declared `receive`
|
|
||||||
method, but it may also be changed using `become` and `unbecome` in
|
|
||||||
response to external messages. All of this contributes to the overall actor
|
|
||||||
behavior and it does not lend itself to easy testing on the `Actor`
|
|
||||||
itself. Therefore the `TestActorRef` offers a different mode of
|
|
||||||
operation to complement the `Actor` testing: it supports all operations
|
|
||||||
also valid on normal `ActorRef`. Messages sent to the actor are
|
|
||||||
processed synchronously on the current thread and answers may be sent back as
|
|
||||||
usual. This trick is made possible by the `CallingThreadDispatcher`
|
|
||||||
described below (see [CallingThreadDispatcher](#callingthreaddispatcher)); this dispatcher is set
|
|
||||||
implicitly for any actor instantiated into a `TestActorRef`.
|
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-behavior }
|
|
||||||
|
|
||||||
As the `TestActorRef` is a subclass of `LocalActorRef` with a few
|
|
||||||
special extras, also aspects like supervision and restarting work properly, but
|
|
||||||
beware that execution is only strictly synchronous as long as all actors
|
|
||||||
involved use the `CallingThreadDispatcher`. As soon as you add elements
|
|
||||||
which include more sophisticated scheduling you leave the realm of unit testing
|
|
||||||
as you then need to think about asynchronicity again (in most cases the problem
|
|
||||||
will be to wait until the desired effect had a chance to happen).
|
|
||||||
|
|
||||||
One more special aspect which is overridden for single-threaded tests is the
|
|
||||||
`receiveTimeout`, as including that would entail asynchronous queuing of
|
|
||||||
`ReceiveTimeout` messages, violating the synchronous contract.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
To summarize: `TestActorRef` overwrites two fields: it sets the
|
|
||||||
dispatcher to `CallingThreadDispatcher.global` and it sets the
|
|
||||||
`receiveTimeout` to None.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
### The Way In-Between: Expecting Exceptions
|
|
||||||
|
|
||||||
If you want to test the actor behavior, including hotswapping, but without
|
|
||||||
involving a dispatcher and without having the `TestActorRef` swallow
|
|
||||||
any thrown exceptions, then there is another mode available for you: just use
|
|
||||||
the `receive` method on `TestActorRef`, which will be forwarded to the
|
|
||||||
underlying actor:
|
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-expecting-exceptions }
|
|
||||||
|
|
||||||
### Use Cases
|
|
||||||
|
|
||||||
You may of course mix and match both modi operandi of `TestActorRef` as
|
|
||||||
suits your test needs:
|
|
||||||
|
|
||||||
* one common use case is setting up the actor into a specific internal state
|
|
||||||
before sending the test message
|
|
||||||
* another is to verify correct internal state transitions after having sent
|
|
||||||
the test message
|
|
||||||
|
|
||||||
Feel free to experiment with the possibilities, and if you find useful
|
|
||||||
patterns, don't hesitate to let the Akka forums know about them! Who knows,
|
|
||||||
common operations might even be worked into nice DSLs.
|
|
||||||
|
|
||||||
<a id="async-integration-testing"></a>
|
|
||||||
## Asynchronous Integration Testing with `TestKit`
|
|
||||||
|
|
||||||
When you are reasonably sure that your actor's business logic is correct, the
|
When you are reasonably sure that your actor's business logic is correct, the
|
||||||
next step is verifying that it works correctly within its intended environment
|
next step is verifying that it works correctly within its intended
|
||||||
(if the individual actors are simple enough, possibly because they use the
|
environment@scala[ (if the individual actors are simple enough, possibly because they use the
|
||||||
`FSM` module, this might also be the first step). The definition of the
|
`FSM` module, this might also be the first step)]. The definition of the
|
||||||
environment depends of course very much on the problem at hand and the level at
|
environment depends of course very much on the problem at hand and the level at
|
||||||
which you intend to test, ranging for functional/integration tests to full
|
which you intend to test, ranging for functional/integration tests to full
|
||||||
system tests. The minimal setup consists of the test procedure, which provides
|
system tests. The minimal setup consists of the test procedure, which provides
|
||||||
|
|
@ -183,141 +31,172 @@ single procedure drives the test.
|
||||||
The `TestKit` class contains a collection of tools which makes this
|
The `TestKit` class contains a collection of tools which makes this
|
||||||
common task easy.
|
common task easy.
|
||||||
|
|
||||||
@@snip [PlainWordSpec.scala]($code$/scala/docs/testkit/PlainWordSpec.scala) { #plain-spec }
|
Scala
|
||||||
|
: @@snip [PlainWordSpec.scala]($code$/scala/docs/testkit/PlainWordSpec.scala) { #plain-spec }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitSampleTest.java]($code$/java/jdocs/testkit/TestKitSampleTest.java) { #fullsample }
|
||||||
|
|
||||||
The `TestKit` contains an actor named `testActor` which is the
|
The `TestKit` contains an actor named `testActor` which is the
|
||||||
entry point for messages to be examined with the various `expectMsg...`
|
entry point for messages to be examined with the various `expectMsg...`
|
||||||
assertions detailed below. When mixing in the trait `ImplicitSender` this
|
assertions detailed below. @scala[When mixing in the trait `ImplicitSender` this
|
||||||
test actor is implicitly used as sender reference when dispatching messages
|
test actor is implicitly used as sender reference when dispatching messages
|
||||||
from the test procedure. The `testActor` may also be passed to
|
from the test procedure.] @java[The test actor’s reference is obtained using the
|
||||||
|
`getRef()` method as demonstrated above.] The `testActor` may also be passed to
|
||||||
other actors as usual, usually subscribing it as notification listener. There
|
other actors as usual, usually subscribing it as notification listener. There
|
||||||
is a whole set of examination methods, e.g. receiving all consecutive messages
|
is a whole set of examination methods, e.g. receiving all consecutive messages
|
||||||
matching certain criteria, receiving a whole sequence of fixed messages or
|
matching certain criteria, receiving a whole sequence of fixed messages or
|
||||||
classes, receiving nothing for some time, etc.
|
classes, receiving nothing for some time, etc.
|
||||||
|
|
||||||
The ActorSystem passed in to the constructor of TestKit is accessible via the
|
The ActorSystem passed in to the constructor of TestKit is accessible via the
|
||||||
`system` member. Remember to shut down the actor system after the test is
|
@scala[`system` member]@java[`getSystem()` method].
|
||||||
|
|
||||||
|
@@@ note
|
||||||
|
|
||||||
|
Remember to shut down the actor system after the test is
|
||||||
finished (also in case of failure) so that all actors—including the test
|
finished (also in case of failure) so that all actors—including the test
|
||||||
actor—are stopped.
|
actor—are stopped.
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
### Built-In Assertions
|
### Built-In Assertions
|
||||||
|
|
||||||
The above mentioned `expectMsg` is not the only method for formulating
|
The above mentioned @scala[`expectMsg`]@java[`expectMsgEquals`] is not the only method for formulating
|
||||||
assertions concerning received messages. Here is the full list:
|
assertions concerning received messages, the full set is this:
|
||||||
|
|
||||||
*
|
Java
|
||||||
`expectMsg[T](d: Duration, msg: T): T`
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-expect }
|
||||||
|
|
||||||
|
In these examples, the maximum durations you will find mentioned below are left
|
||||||
|
out, in which case they use the default value from configuration item
|
||||||
|
`akka.test.single-expect-default` which itself defaults to 3 seconds (or they
|
||||||
|
obey the innermost enclosing `Within` as detailed [below](#testkit-within)). The full signatures are:
|
||||||
|
|
||||||
|
* @scala[`expectMsg[T](d: Duration, msg: T): T`]@java[`public <T> T expectMsgEquals(FiniteDuration max, T msg)`]
|
||||||
The given message object must be received within the specified time; the
|
The given message object must be received within the specified time; the
|
||||||
object will be returned.
|
object will be returned.
|
||||||
*
|
* @scala[`expectMsgPF[T](d: Duration)(pf: PartialFunction[Any, T]): T`]@java[`public <T> T expectMsgPF(Duration max, String hint, Function<Object, T> f)`]
|
||||||
`expectMsgPF[T](d: Duration)(pf: PartialFunction[Any, T]): T`
|
|
||||||
Within the given time period, a message must be received and the given
|
Within the given time period, a message must be received and the given
|
||||||
partial function must be defined for that message; the result from applying
|
@scala[partial] function must be defined for that message; the result from applying
|
||||||
the partial function to the received message is returned. The duration may
|
the @scala[partial] function to the received message is returned. @scala[The duration may
|
||||||
be left unspecified (empty parentheses are required in this case) to use
|
be left unspecified (empty parentheses are required in this case) to use
|
||||||
the deadline from the innermost enclosing [within](#testkit-within)
|
the deadline from the innermost enclosing [within](#testkit-within)
|
||||||
block instead.
|
block instead.]
|
||||||
*
|
* @scala[`expectMsgClass[T](d: Duration, c: Class[T]): T`]@java[`public <T> T expectMsgClass(FiniteDuration max, Class<T> c)`]
|
||||||
`expectMsgClass[T](d: Duration, c: Class[T]): T`
|
|
||||||
An object which is an instance of the given `Class` must be received
|
An object which is an instance of the given `Class` must be received
|
||||||
within the allotted time frame; the object will be returned. Note that this
|
within the allotted time frame; the object will be returned. Note that this
|
||||||
does a conformance check; if you need the class to be equal, have a look at
|
does a conformance check; if you need the class to be equal, @scala[have a look at
|
||||||
`expectMsgAllClassOf` with a single given class argument.
|
`expectMsgAllClassOf` with a single given class argument]@java[you need to verify that afterwards].
|
||||||
*
|
|
||||||
`expectMsgType[T: Manifest](d: Duration)`
|
@@@ div { .group-scala }
|
||||||
|
|
||||||
|
* `expectMsgType[T: Manifest](d: Duration)`
|
||||||
An object which is an instance of the given type (after erasure) must be
|
An object which is an instance of the given type (after erasure) must be
|
||||||
received within the allotted time frame; the object will be returned. This
|
received within the allotted time frame; the object will be returned. This
|
||||||
method is approximately equivalent to
|
method is approximately equivalent to
|
||||||
`expectMsgClass(implicitly[ClassTag[T]].runtimeClass)`.
|
`expectMsgClass(implicitly[ClassTag[T]].runtimeClass)`.]
|
||||||
*
|
|
||||||
`expectMsgAnyOf[T](d: Duration, obj: T*): T`
|
@@@
|
||||||
|
|
||||||
|
* @scala[`expectMsgAnyOf[T](d: Duration, obj: T*): T`]@java[`public Object expectMsgAnyOf(Duration max, Object... msg)`]
|
||||||
An object must be received within the given time, and it must be equal (
|
An object must be received within the given time, and it must be equal (
|
||||||
compared with `==`) to at least one of the passed reference objects; the
|
compared with @scala[`==`]@java[`equals()`]) to at least one of the passed reference objects; the
|
||||||
received object will be returned.
|
received object will be returned.
|
||||||
*
|
* @scala[`expectMsgAnyClassOf[T](d: Duration, obj: Class[_ <: T]*): T`]@java[`public <T> T expectMsgAnyClassOf(FiniteDuration max, Class<? extends T>... c)`]
|
||||||
`expectMsgAnyClassOf[T](d: Duration, obj: Class[_ <: T]*): T`
|
|
||||||
An object must be received within the given time, and it must be an
|
An object must be received within the given time, and it must be an
|
||||||
instance of at least one of the supplied `Class` objects; the
|
instance of at least one of the supplied `Class` objects; the
|
||||||
received object will be returned.
|
received object will be returned. Note that this does a conformance check,
|
||||||
*
|
if you need the class to be equal you need to verify that afterwards.
|
||||||
`expectMsgAllOf[T](d: Duration, obj: T*): Seq[T]`
|
* @scala[`expectMsgAllOf[T](d: Duration, obj: T*): Seq[T]`]@java[`public List<Object> expectMsgAllOf(FiniteDuration max, Object... msg)`]
|
||||||
A number of objects matching the size of the supplied object array must be
|
A number of objects matching the size of the supplied object array must be
|
||||||
received within the given time, and for each of the given objects there
|
received within the given time, and for each of the given objects there
|
||||||
must exist at least one among the received ones which equals (compared with
|
must exist at least one among the received ones which equals (compared with
|
||||||
`==`) it. The full sequence of received objects is returned.
|
@scala[`==`]@java[`equals()`]) it. The full sequence of received objects is returned in
|
||||||
*
|
the order received.
|
||||||
`expectMsgAllClassOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
|
|
||||||
|
@@@ div { .group-scala }
|
||||||
|
|
||||||
|
* `expectMsgAllClassOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
|
||||||
A number of objects matching the size of the supplied `Class` array
|
A number of objects matching the size of the supplied `Class` array
|
||||||
must be received within the given time, and for each of the given classes
|
must be received within the given time, and for each of the given classes
|
||||||
there must exist at least one among the received objects whose class equals
|
there must exist at least one among the received objects whose class equals
|
||||||
(compared with `==`) it (this is *not* a conformance check). The full
|
(compared with `==`) it (this is *not* a conformance check). The full
|
||||||
sequence of received objects is returned.
|
sequence of received objects is returned.
|
||||||
*
|
* `expectMsgAllConformingOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
|
||||||
`expectMsgAllConformingOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
|
|
||||||
A number of objects matching the size of the supplied `Class` array
|
A number of objects matching the size of the supplied `Class` array
|
||||||
must be received within the given time, and for each of the given classes
|
must be received within the given time, and for each of the given classes
|
||||||
there must exist at least one among the received objects which is an
|
there must exist at least one among the received objects which is an
|
||||||
instance of this class. The full sequence of received objects is returned.
|
instance of this class. The full sequence of received objects is returned.
|
||||||
*
|
|
||||||
`expectNoMsg(d: Duration)`
|
@@@
|
||||||
|
|
||||||
|
* @scala[`expectNoMsg(d: Duration)`]@java[`public void expectNoMsg(FiniteDuration max)`]
|
||||||
No message must be received within the given time. This also fails if a
|
No message must be received within the given time. This also fails if a
|
||||||
message has been received before calling this method which has not been
|
message has been received before calling this method which has not been
|
||||||
removed from the queue using one of the other methods.
|
removed from the queue using one of the other methods.
|
||||||
*
|
* @scala[`receiveN(n: Int, d: Duration): Seq[AnyRef]`]@java[`List<Object> receiveN(int n, FiniteDuration max)`]
|
||||||
`receiveN(n: Int, d: Duration): Seq[AnyRef]`
|
|
||||||
`n` messages must be received within the given time; the received
|
`n` messages must be received within the given time; the received
|
||||||
messages are returned.
|
messages are returned.
|
||||||
*
|
|
||||||
`fishForMessage(max: Duration, hint: String)(pf: PartialFunction[Any, Boolean]): Any`
|
@@@ div { .group-scala }
|
||||||
|
|
||||||
|
* `fishForMessage(max: Duration, hint: String)(pf: PartialFunction[Any, Boolean]): Any`
|
||||||
Keep receiving messages as long as the time is not used up and the partial
|
Keep receiving messages as long as the time is not used up and the partial
|
||||||
function matches and returns `false`. Returns the message received for
|
function matches and returns `false`. Returns the message received for
|
||||||
which it returned `true` or throws an exception, which will include the
|
which it returned `true` or throws an exception, which will include the
|
||||||
provided hint for easier debugging.
|
provided hint for easier debugging.
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
In addition to message reception assertions there are also methods which help
|
In addition to message reception assertions there are also methods which help
|
||||||
with message flows:
|
with message flows:
|
||||||
|
|
||||||
*
|
@@@ div { .group-scala }
|
||||||
`receiveOne(d: Duration): AnyRef`
|
|
||||||
Tries to receive one message for at most the given time interval and
|
* `receiveOne(d: Duration): AnyRef` Tries to receive one message for at most the given time interval and
|
||||||
returns `null` in case of failure. If the given Duration is zero, the
|
returns `null` in case of failure. If the given Duration is zero, the
|
||||||
call is non-blocking (polling mode).
|
call is non-blocking (polling mode).
|
||||||
*
|
|
||||||
`receiveWhile[T](max: Duration, idle: Duration, messages: Int)(pf: PartialFunction[Any, T]): Seq[T]`
|
@@@
|
||||||
Collect messages as long as
|
|
||||||
|
* @scala[`receiveWhile[T](max: Duration, idle: Duration, messages: Int)(pf: PartialFunction[Any, T]): Seq[T]`]@java[`public <T> List<T> receiveWhile(Duration max, Duration idle, Int messages, Function<Object, T> f)`]
|
||||||
|
Collect messages as long as
|
||||||
* they are matching the given partial function
|
* they are matching the given partial function
|
||||||
* the given time interval is not used up
|
* the given time interval is not used up
|
||||||
* the next message is received within the idle timeout
|
* the next message is received within the idle timeout
|
||||||
* the number of messages has not yet reached the maximum
|
* the number of messages has not yet reached the maximum
|
||||||
All collected messages are returned. The maximum duration defaults to the
|
All collected messages are returned. @scala[The maximum duration defaults to the
|
||||||
time remaining in the innermost enclosing [within](#testkit-within)
|
time remaining in the innermost enclosing [within](#testkit-within)
|
||||||
block and the idle duration defaults to infinity (thereby disabling the
|
block and the idle duration defaults to infinity (thereby disabling the
|
||||||
idle timeout feature). The number of expected messages defaults to
|
idle timeout feature). The number of expected messages defaults to
|
||||||
`Int.MaxValue`, which effectively disables this limit.
|
`Int.MaxValue`, which effectively disables this limit.]
|
||||||
*
|
|
||||||
`awaitCond(p: => Boolean, max: Duration, interval: Duration)`
|
* @scala[`awaitCond(p: => Boolean, max: Duration, interval: Duration)`]@java[`public void awaitCond(Duration max, Duration interval, Supplier<Boolean> p)`]
|
||||||
Poll the given condition every `interval` until it returns `true` or
|
Poll the given condition every `interval` until it returns `true` or
|
||||||
the `max` duration is used up. The interval defaults to 100 ms and the
|
the `max` duration is used up. @scala[The interval defaults to 100 ms and the
|
||||||
maximum defaults to the time remaining in the innermost enclosing
|
maximum defaults to the time remaining in the innermost enclosing
|
||||||
[within](#testkit-within) block.
|
[within](#testkit-within) block.]
|
||||||
*
|
|
||||||
`awaitAssert(a: => Any, max: Duration, interval: Duration)`
|
* @scala[`awaitAssert(a: => Any, max: Duration, interval: Duration)`]@java[`public void awaitAssert(Duration max, Duration interval, Supplier<Object> a)`]
|
||||||
Poll the given assert function every `interval` until it does not throw
|
Poll the given assert function every `interval` until it does not throw
|
||||||
an exception or the `max` duration is used up. If the timeout expires the
|
an exception or the `max` duration is used up. If the timeout expires the
|
||||||
last exception is thrown. The interval defaults to 100 ms and the maximum defaults
|
last exception is thrown. @scala[The interval defaults to 100 ms and the maximum defaults
|
||||||
to the time remaining in the innermost enclosing [within](#testkit-within)
|
to the time remaining in the innermost enclosing [within](#testkit-within)
|
||||||
block.The interval defaults to 100 ms and the maximum defaults to the time
|
block. The interval defaults to 100 ms and the maximum defaults to the time
|
||||||
remaining in the innermost enclosing [within](#testkit-within) block.
|
remaining in the innermost enclosing [within](#testkit-within) block.]
|
||||||
*
|
|
||||||
`ignoreMsg(pf: PartialFunction[AnyRef, Boolean])`
|
* @scala[`ignoreMsg(pf: PartialFunction[AnyRef, Boolean])`]@java[`public void ignoreMsg(Function<Object, Boolean> f)`]
|
||||||
`ignoreNoMsg`
|
@scala[`ignoreMsg`]@java[`public void ignoreMsg()`]
|
||||||
The internal `testActor` contains a partial function for ignoring
|
@java[There are also cases where not all messages sent to the test kit are actually
|
||||||
|
relevant to the test, but removing them would mean altering the actors under
|
||||||
|
test. For this purpose it is possible to ignore certain messages.]
|
||||||
|
@scala[The internal `testActor` contains a partial function for ignoring
|
||||||
messages: it will only enqueue messages which do not match the function or
|
messages: it will only enqueue messages which do not match the function or
|
||||||
for which the function returns `false`. This function can be set and
|
for which the function returns `false`. This function can be set and
|
||||||
reset using the methods given above; each invocation replaces the previous
|
reset using the methods given above; each invocation replaces the previous
|
||||||
function, they are not composed.
|
function, they are not composed.
|
||||||
This feature is useful e.g. when testing a logging system, where you want
|
This feature is useful e.g. when testing a logging system, where you want
|
||||||
to ignore regular messages and are only interested in your specific ones.
|
to ignore regular messages and are only interested in your specific ones.]
|
||||||
|
|
||||||
### Expecting Log Messages
|
### Expecting Log Messages
|
||||||
|
|
||||||
|
|
@ -328,7 +207,11 @@ handler with the `TestEventListener` and using an `EventFilter`
|
||||||
allows assertions on log messages, including those which are generated by
|
allows assertions on log messages, including those which are generated by
|
||||||
exceptions:
|
exceptions:
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #event-filter }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #event-filter }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-event-filter }
|
||||||
|
|
||||||
If a number of occurrences is specific—as demonstrated above—then `intercept`
|
If a number of occurrences is specific—as demonstrated above—then `intercept`
|
||||||
will block until that number of matching messages have been received or the
|
will block until that number of matching messages have been received or the
|
||||||
|
|
@ -358,13 +241,17 @@ the positive or negative result must be obtained. Lower time limits need to be
|
||||||
checked external to the examination, which is facilitated by a new construct
|
checked external to the examination, which is facilitated by a new construct
|
||||||
for managing time constraints:
|
for managing time constraints:
|
||||||
|
|
||||||
```scala
|
Scala
|
||||||
|
: ```scala
|
||||||
within([min, ]max) {
|
within([min, ]max) {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The block given to `within` must complete after a @ref:[Duration](common/duration.md) which
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-within }
|
||||||
|
|
||||||
|
The block @scala[given to]@java[in] `within` must complete after a @ref:[Duration](common/duration.md) which
|
||||||
is between `min` and `max`, where the former defaults to zero. The
|
is between `min` and `max`, where the former defaults to zero. The
|
||||||
deadline calculated by adding the `max` parameter to the block's start
|
deadline calculated by adding the `max` parameter to the block's start
|
||||||
time is implicitly available within the block to all examination methods, if
|
time is implicitly available within the block to all examination methods, if
|
||||||
|
|
@ -377,18 +264,23 @@ It should be noted that if the last message-receiving assertion of the block is
|
||||||
latencies. This means that while individual contained assertions still use the
|
latencies. This means that while individual contained assertions still use the
|
||||||
maximum time bound, the overall block may take arbitrarily longer in this case.
|
maximum time bound, the overall block may take arbitrarily longer in this case.
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-within }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-within }
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
||||||
All times are measured using `System.nanoTime`, meaning that they describe
|
All times are measured using `System.nanoTime`, meaning that they describe
|
||||||
wall time, not CPU time.
|
wall time, not CPU time or system time.
|
||||||
|
|
||||||
@@@
|
@@@
|
||||||
|
|
||||||
|
@@@ div { .group-scala }
|
||||||
|
|
||||||
Ray Roestenburg has written a great article on using the TestKit:
|
Ray Roestenburg has written a great article on using the TestKit:
|
||||||
[http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html](http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html).
|
[http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html](http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html).
|
||||||
His full example is also available @ref:[here](testkit-example.md).
|
His full example is also available @ref:[here](testing.md#example).
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
#### Accounting for Slow Test Systems
|
#### Accounting for Slow Test Systems
|
||||||
|
|
||||||
|
|
@ -398,10 +290,16 @@ invariably lead to spurious test failures on the heavily loaded Jenkins server
|
||||||
internally scaled by a factor taken from the [Configuration](),
|
internally scaled by a factor taken from the [Configuration](),
|
||||||
`akka.test.timefactor`, which defaults to 1.
|
`akka.test.timefactor`, which defaults to 1.
|
||||||
|
|
||||||
You can scale other durations with the same factor by using the implicit conversion
|
You can scale other durations with the same factor by using the @scala[implicit conversion
|
||||||
in `akka.testkit` package object to add dilated function to `Duration`.
|
in `akka.testkit` package object to add dilated function to `Duration`]@java[`dilated` method in `TestKit`].
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #duration-dilation }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #duration-dilation }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #duration-dilation }
|
||||||
|
|
||||||
|
@@@ div { .group-scala }
|
||||||
|
|
||||||
### Resolving Conflicts with Implicit ActorRef
|
### Resolving Conflicts with Implicit ActorRef
|
||||||
|
|
||||||
|
|
@ -410,25 +308,30 @@ simply mix in `ImplicitSender` into your test.
|
||||||
|
|
||||||
@@snip [PlainWordSpec.scala]($code$/scala/docs/testkit/PlainWordSpec.scala) { #implicit-sender }
|
@@snip [PlainWordSpec.scala]($code$/scala/docs/testkit/PlainWordSpec.scala) { #implicit-sender }
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
### Using Multiple Probe Actors
|
### Using Multiple Probe Actors
|
||||||
|
|
||||||
When the actors under test are supposed to send various messages to different
|
When the actors under test are supposed to send various messages to different
|
||||||
destinations, it may be difficult distinguishing the message streams arriving
|
destinations, it may be difficult distinguishing the message streams arriving
|
||||||
at the `testActor` when using the `TestKit` as a mixin. Another
|
at the `testActor` when using the `TestKit` as @scala[a mixin]@java[shown until now]. Another
|
||||||
approach is to use it for creation of simple probe actors to be inserted in the
|
approach is to use it for creation of simple probe actors to be inserted in the
|
||||||
message flows. To make this more powerful and convenient, there is a concrete
|
message flows. @scala[To make this more powerful and convenient, there is a concrete
|
||||||
implementation called `TestProbe`. The functionality is best explained
|
implementation called `TestProbe`.] The functionality is best explained
|
||||||
using a small example:
|
using a small example:
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #imports-test-probe }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #imports-test-probe }
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #my-double-echo }
|
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #my-double-echo }
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe }
|
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe }
|
||||||
|
|
||||||
Here a the system under test is simulated by `MyDoubleEcho`, which is
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe }
|
||||||
|
|
||||||
|
@scala[Here the system under test is simulated by `MyDoubleEcho`, which is
|
||||||
supposed to mirror its input to two outputs. Attaching two test probes enables
|
supposed to mirror its input to two outputs. Attaching two test probes enables
|
||||||
verification of the (simplistic) behavior. Another example would be two actors
|
verification of the (simplistic) behavior]@java[This simple test verifies an equally simple Forwarder actor by injecting a
|
||||||
|
probe as the forwarder’s target]. Another example would be two actors
|
||||||
A and B which collaborate by A sending messages to B. In order to verify this
|
A and B which collaborate by A sending messages to B. In order to verify this
|
||||||
message flow, a `TestProbe` could be inserted as target of A, using the
|
message flow, a `TestProbe` could be inserted as target of A, using the
|
||||||
forwarding capabilities or auto-pilot described below to include a real B in
|
forwarding capabilities or auto-pilot described below to include a real B in
|
||||||
|
|
@ -437,12 +340,20 @@ the test setup.
|
||||||
If you have many test probes, you can name them to get meaningful actor names
|
If you have many test probes, you can name them to get meaningful actor names
|
||||||
in test logs and assertions:
|
in test logs and assertions:
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-with-custom-name }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-with-custom-name }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe-with-custom-name }
|
||||||
|
|
||||||
Probes may also be equipped with custom assertions to make your test code even
|
Probes may also be equipped with custom assertions to make your test code even
|
||||||
more concise and clear:
|
more concise and clear:
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-special-probe }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-special-probe }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-special-probe }
|
||||||
|
|
||||||
You have complete flexibility here in mixing and matching the `TestKit`
|
You have complete flexibility here in mixing and matching the `TestKit`
|
||||||
facilities with your own checks and choosing an intuitive name for it. In real
|
facilities with your own checks and choosing an intuitive name for it. In real
|
||||||
|
|
@ -462,31 +373,46 @@ means that it is dangerous to try watching e.g. `TestActorRef` from a
|
||||||
|
|
||||||
#### Watching Other Actors from Probes
|
#### Watching Other Actors from Probes
|
||||||
|
|
||||||
A `TestProbe` can register itself for DeathWatch of any other actor:
|
A @scala[`TestProbe`]@java[`TestKit`] can register itself for DeathWatch of any other actor:
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-watch }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-watch }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe-watch }
|
||||||
|
|
||||||
#### Replying to Messages Received by Probes
|
#### Replying to Messages Received by Probes
|
||||||
|
|
||||||
The probes keep track of the communications channel for replies, if possible,
|
@scala[The probes keep track of the communications channel for replies, if possible,
|
||||||
so they can also reply:
|
so they can also reply]@java[The probe stores the sender of the last dequeued message (i.e. after its
|
||||||
|
`expectMsg*` reception), which may be retrieved using the
|
||||||
|
`getLastSender()` method. This information can also implicitly be used
|
||||||
|
for having the probe reply to the last received message]:
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-reply }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-reply }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe-reply }
|
||||||
|
|
||||||
#### Forwarding Messages Received by Probes
|
#### Forwarding Messages Received by Probes
|
||||||
|
|
||||||
Given a destination actor `dest` which in the nominal actor network would
|
@scala[Given a destination actor `dest` which in the nominal actor network would
|
||||||
receive a message from actor `source`. If you arrange for the message to be
|
receive a message from actor `source`. If you arrange for the message to be
|
||||||
sent to a `TestProbe` `probe` instead, you can make assertions
|
sent to a `TestProbe` `probe` instead, you can make assertions
|
||||||
concerning volume and timing of the message flow while still keeping the
|
concerning volume and timing of the message flow while still keeping the
|
||||||
network functioning:
|
network functioning]@java[The probe can also forward a received message (i.e. after its `expectMsg*`
|
||||||
|
reception), retaining the original sender]:
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-forward-actors }
|
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-forward-actors }
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-forward }
|
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-probe-forward }
|
||||||
|
|
||||||
The `dest` actor will receive the same message invocation as if no test probe
|
Java
|
||||||
had intervened.
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-probe-forward }
|
||||||
|
|
||||||
|
@scala[The `dest` actor will receive the same message invocation as if no test probe
|
||||||
|
had intervened.]
|
||||||
|
|
||||||
#### Auto-Pilot
|
#### Auto-Pilot
|
||||||
|
|
||||||
|
|
@ -497,11 +423,16 @@ keep a test running and verify traces later you can also install an
|
||||||
This code can be used to forward messages, e.g. in a chain `A --> Probe -->
|
This code can be used to forward messages, e.g. in a chain `A --> Probe -->
|
||||||
B`, as long as a certain protocol is obeyed.
|
B`, as long as a certain protocol is obeyed.
|
||||||
|
|
||||||
@@snip [TestProbeSpec.scala]($akka$/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala) { #autopilot }
|
Scala
|
||||||
|
: @@snip [TestProbeSpec.scala]($akka$/akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala) { #autopilot }
|
||||||
|
|
||||||
The `run` method must return the auto-pilot for the next message, which
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-auto-pilot }
|
||||||
|
|
||||||
|
The `run` method must return the auto-pilot for the next message, @scala[which
|
||||||
may be `KeepRunning` to retain the current one or `NoAutoPilot`
|
may be `KeepRunning` to retain the current one or `NoAutoPilot`
|
||||||
to switch it off.
|
to switch it off]@java[wrapped
|
||||||
|
in an `Option`; setting it to `None` terminates the auto-pilot].
|
||||||
|
|
||||||
#### Caution about Timing Assertions
|
#### Caution about Timing Assertions
|
||||||
|
|
||||||
|
|
@ -511,9 +442,13 @@ described [above](#testkit-within) is local to each probe. Hence, probes
|
||||||
do not react to each other's deadlines or to the deadline set in an enclosing
|
do not react to each other's deadlines or to the deadline set in an enclosing
|
||||||
`TestKit` instance:
|
`TestKit` instance:
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-within-probe }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-within-probe }
|
||||||
|
|
||||||
Here, the `expectMsg` call will use the default timeout.
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-within-probe }
|
||||||
|
|
||||||
|
Here, the @scala[`expectMsg`]@java[`expectMsgEquals`] call will use the default timeout.
|
||||||
|
|
||||||
### Testing parent-child relationships
|
### Testing parent-child relationships
|
||||||
|
|
||||||
|
|
@ -530,53 +465,84 @@ Conversely, a parent's binding to its child can be lessened as follows:
|
||||||
|
|
||||||
4. when creating a parent, tell the parent how to create its child
|
4. when creating a parent, tell the parent how to create its child
|
||||||
|
|
||||||
For example, the structure of the code you want to test may follow this pattern:
|
For example, the structure of the code you want to test may follow this pattern:
|
||||||
|
|
||||||
@@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-example }
|
Scala
|
||||||
|
: @@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-example }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-example }
|
||||||
|
|
||||||
#### Introduce child to its parent
|
#### Introduce child to its parent
|
||||||
|
|
||||||
The first option is to avoid use of the `context.parent` function and create
|
The first option is to avoid use of the `context.parent` function and create
|
||||||
a child with a custom parent by passing an explicit reference to its parent instead.
|
a child with a custom parent by passing an explicit reference to its parent instead.
|
||||||
|
|
||||||
@@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-dependentchild }
|
Scala
|
||||||
|
: @@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-dependentchild }
|
||||||
|
|
||||||
#### Create the child using TestProbe
|
Java
|
||||||
|
: @@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-dependentchild }
|
||||||
|
|
||||||
The `TestProbe` class can in fact create actors that will run with the test probe as parent.
|
#### Create the child using @scala[TestProbe]@java[TestKit]
|
||||||
This will cause any messages the child actor sends to *context.parent* to
|
|
||||||
|
The @scala[`TestProbe`]@java[`TestKit`] class can in fact create actors that will run with the test probe as parent.
|
||||||
|
This will cause any messages the child actor sends to @scala[*context.parent*]@java[*getContext().getParent()*] to
|
||||||
end up in the test probe.
|
end up in the test probe.
|
||||||
|
|
||||||
@@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-TestProbe-parent }
|
Scala
|
||||||
|
: @@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-TestProbe-parent }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-TestProbe-parent }
|
||||||
|
|
||||||
#### Using a fabricated parent
|
#### Using a fabricated parent
|
||||||
|
|
||||||
If you prefer to avoid modifying the parent or child constructor you can
|
If you prefer to avoid modifying the parent or child constructor you can
|
||||||
create a fabricated parent in your test. This, however, does not enable you to test
|
create a fabricated parent in your test. This, however, does not enable you to test
|
||||||
the parent actor in isolation.
|
the parent actor in isolation.
|
||||||
|
|
||||||
@@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-fabricated-parent }
|
Scala
|
||||||
|
: @@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-fabricated-parent }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-fabricated-parent-creator }
|
||||||
|
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-fabricated-parent }
|
||||||
|
|
||||||
#### Externalize child making from the parent
|
#### Externalize child making from the parent
|
||||||
|
|
||||||
Alternatively, you can tell the parent how to create its child. There are two ways
|
Alternatively, you can tell the parent how to create its child. There are two ways
|
||||||
to do this: by giving it a `Props` object or by giving it a function which takes care of creating the child actor:
|
to do this: by giving it a `Props` object or by giving it a function which takes care of creating the child actor:
|
||||||
|
|
||||||
@@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-dependentparent }
|
Scala
|
||||||
|
: @@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #test-dependentparent }
|
||||||
|
|
||||||
Creating the `Props` is straightforward and the function may look like this in your test code:
|
Java
|
||||||
|
: @@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-dependentparent }
|
||||||
|
@@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #test-dependentparent-generic }
|
||||||
|
|
||||||
@@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #child-maker-test }
|
|
||||||
|
Creating the @scala[`Props`]@java[`Actor`] is straightforward and the function may look like this in your test code:
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #child-maker-test }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #child-maker-test }
|
||||||
|
|
||||||
And like this in your application code:
|
And like this in your application code:
|
||||||
|
|
||||||
@@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #child-maker-prod }
|
Scala
|
||||||
|
: @@snip [ParentChildSpec.scala]($code$/scala/docs/testkit/ParentChildSpec.scala) { #child-maker-prod }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [ParentChildTest.java]($code$/java/jdocs/testkit/ParentChildTest.java) { #child-maker-prod }
|
||||||
|
|
||||||
Which of these methods is the best depends on what is most important to test. The
|
Which of these methods is the best depends on what is most important to test. The
|
||||||
most generic option is to create the parent actor by passing it a function that is
|
most generic option is to create the parent actor by passing it a function that is
|
||||||
responsible for the Actor creation, but the fabricated parent is often sufficient.
|
responsible for the Actor creation, but @scala[the]@java[using `TestProbe` or having a] fabricated parent is often sufficient.
|
||||||
|
|
||||||
<a id="scala-callingthreaddispatcher"></a>
|
<a id="callingthreaddispatcher"></a>
|
||||||
## CallingThreadDispatcher
|
## CallingThreadDispatcher
|
||||||
|
|
||||||
The `CallingThreadDispatcher` serves good purposes in unit testing, as
|
The `CallingThreadDispatcher` serves good purposes in unit testing, as
|
||||||
|
|
@ -590,7 +556,11 @@ so long as all intervening actors run on this dispatcher.
|
||||||
|
|
||||||
Just set the dispatcher as you normally would:
|
Just set the dispatcher as you normally would:
|
||||||
|
|
||||||
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #calling-thread-dispatcher }
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #calling-thread-dispatcher }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #calling-thread-dispatcher }
|
||||||
|
|
||||||
### How it works
|
### How it works
|
||||||
|
|
||||||
|
|
@ -714,12 +684,12 @@ to find the cause, fix it and verify the test again. This process is supported
|
||||||
by debuggers as well as logging, where the Akka toolkit offers the following
|
by debuggers as well as logging, where the Akka toolkit offers the following
|
||||||
options:
|
options:
|
||||||
|
|
||||||
*
|
* *Logging of exceptions thrown within Actor instances*
|
||||||
*Logging of exceptions thrown within Actor instances*
|
|
||||||
This is always on; in contrast to the other logging mechanisms, this logs at
|
This is always on; in contrast to the other logging mechanisms, this logs at
|
||||||
`ERROR` level.
|
`ERROR` level.
|
||||||
*
|
|
||||||
*Logging of message invocations on certain actors*
|
@@@ div { .group-scala }
|
||||||
|
* *Logging of message invocations on certain actors*
|
||||||
This is enabled by a setting in the [Configuration]() — namely
|
This is enabled by a setting in the [Configuration]() — namely
|
||||||
`akka.actor.debug.receive` — which enables the `loggable`
|
`akka.actor.debug.receive` — which enables the `loggable`
|
||||||
statement to be applied to an actor’s `receive` function:
|
statement to be applied to an actor’s `receive` function:
|
||||||
|
|
@ -734,14 +704,14 @@ The logging feature is coupled to this specific local mark-up because
|
||||||
enabling it uniformly on all actors is not usually what you need, and it
|
enabling it uniformly on all actors is not usually what you need, and it
|
||||||
would lead to endless loops if it were applied to event bus logger listeners.
|
would lead to endless loops if it were applied to event bus logger listeners.
|
||||||
|
|
||||||
*
|
@@@
|
||||||
*Logging of special messages*
|
|
||||||
|
* *Logging of special messages*
|
||||||
Actors handle certain special messages automatically, e.g. `Kill`,
|
Actors handle certain special messages automatically, e.g. `Kill`,
|
||||||
`PoisonPill`, etc. Tracing of these message invocations is enabled by
|
`PoisonPill`, etc. Tracing of these message invocations is enabled by
|
||||||
the setting `akka.actor.debug.autoreceive`, which enables this on all
|
the setting `akka.actor.debug.autoreceive`, which enables this on all
|
||||||
actors.
|
actors.
|
||||||
*
|
* *Logging of the actor lifecycle*
|
||||||
*Logging of the actor lifecycle*
|
|
||||||
Actor creation, start, restart, monitor start, monitor stop and stop may be traced by
|
Actor creation, start, restart, monitor start, monitor stop and stop may be traced by
|
||||||
enabling the setting `akka.actor.debug.lifecycle`; this, too, is enabled
|
enabling the setting `akka.actor.debug.lifecycle`; this, too, is enabled
|
||||||
uniformly on all actors.
|
uniformly on all actors.
|
||||||
|
|
@ -762,6 +732,8 @@ akka {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@@ div { .group-scala }
|
||||||
|
|
||||||
## Different Testing Frameworks
|
## Different Testing Frameworks
|
||||||
|
|
||||||
Akka’s own test suite is written using [ScalaTest](http://scalatest.org),
|
Akka’s own test suite is written using [ScalaTest](http://scalatest.org),
|
||||||
|
|
@ -784,13 +756,9 @@ The `implicit lazy val system` must be declared exactly like that (you can of
|
||||||
course pass arguments to the actor system factory as needed) because trait
|
course pass arguments to the actor system factory as needed) because trait
|
||||||
`TestKitBase` needs the system during its construction.
|
`TestKitBase` needs the system during its construction.
|
||||||
|
|
||||||
@@@ warning
|
Warning: use of the trait is discouraged because of potential issues with binary
|
||||||
|
|
||||||
Use of the trait is discouraged because of potential issues with binary
|
|
||||||
backwards compatibility in the future, use at own risk.
|
backwards compatibility in the future, use at own risk.
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
### Specs2
|
### Specs2
|
||||||
|
|
||||||
Some [Specs2](http://specs2.org) users have contributed examples of how to work around some clashes which may arise:
|
Some [Specs2](http://specs2.org) users have contributed examples of how to work around some clashes which may arise:
|
||||||
|
|
@ -812,7 +780,163 @@ Specs2, which is not justified by this little feature.
|
||||||
* Specifications are by default executed concurrently, which requires some care
|
* Specifications are by default executed concurrently, which requires some care
|
||||||
when writing the tests or alternatively the `sequential` keyword.
|
when writing the tests or alternatively the `sequential` keyword.
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
There are several configuration properties for the TestKit module, please refer
|
There are several configuration properties for the TestKit module, please refer
|
||||||
to the @ref:[reference configuration](general/configuration.md#config-akka-testkit).
|
to the @ref:[reference configuration](general/configuration.md#config-akka-testkit).
|
||||||
|
|
||||||
|
@@@ div { .group-scala }
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Ray Roestenburg's example code from [his blog](http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html) adapted to work with Akka 2.x.
|
||||||
|
|
||||||
|
@@snip [TestKitUsageSpec.scala]($code$/scala/docs/testkit/TestKitUsageSpec.scala) { #testkit-usage }
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
|
## Synchronous Testing: `TestActorRef`
|
||||||
|
|
||||||
|
Testing the business logic inside `Actor` classes can be divided into
|
||||||
|
two parts: first, each atomic operation must work in isolation, then sequences
|
||||||
|
of incoming events must be processed correctly, even in the presence of some
|
||||||
|
possible variability in the ordering of events. The former is the primary use
|
||||||
|
case for single-threaded unit testing, while the latter can only be verified in
|
||||||
|
integration tests.
|
||||||
|
|
||||||
|
Normally, the `ActorRef` shields the underlying `Actor` instance
|
||||||
|
from the outside, the only communications channel is the actor's mailbox. This
|
||||||
|
restriction is an impediment to unit testing, which led to the inception of the
|
||||||
|
`TestActorRef`. This special type of reference is designed specifically
|
||||||
|
for test purposes and allows access to the actor in two ways: either by
|
||||||
|
obtaining a reference to the underlying actor instance, or by invoking or
|
||||||
|
querying the actor's behaviour (`receive`). Each one warrants its own
|
||||||
|
section below.
|
||||||
|
|
||||||
|
@@@ note
|
||||||
|
|
||||||
|
It is highly recommended to stick to traditional behavioural testing (using messaging
|
||||||
|
to ask the Actor to reply with the state you want to run assertions against),
|
||||||
|
instead of using `TestActorRef` whenever possible.
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
|
@@@ warning
|
||||||
|
|
||||||
|
Due to the synchronous nature of `TestActorRef` it will **not** work with some support
|
||||||
|
traits that Akka provides as they require asynchronous behaviours to function properly.
|
||||||
|
Examples of traits that do not mix well with test actor refs are @ref:[PersistentActor](persistence.md#event-sourcing)
|
||||||
|
and @ref:[AtLeastOnceDelivery](persistence.md#at-least-once-delivery) provided by @ref:[Akka Persistence](persistence.md).
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
|
### Obtaining a Reference to an `Actor`
|
||||||
|
|
||||||
|
Having access to the actual `Actor` object allows application of all
|
||||||
|
traditional unit testing techniques on the contained methods. Obtaining a
|
||||||
|
reference is done like this:
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-actor-ref }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-actor-ref }
|
||||||
|
|
||||||
|
Since `TestActorRef` is generic in the actor type it returns the
|
||||||
|
underlying actor with its proper static type. From this point on you may bring
|
||||||
|
any unit testing tool to bear on your actor as usual.
|
||||||
|
|
||||||
|
@@@ div { .group-scala }
|
||||||
|
|
||||||
|
|
||||||
|
<a id="testfsmref"></a>
|
||||||
|
### Testing Finite State Machines
|
||||||
|
|
||||||
|
If your actor under test is a `FSM`, you may use the special
|
||||||
|
`TestFSMRef` which offers all features of a normal `TestActorRef`
|
||||||
|
and in addition allows access to the internal state:
|
||||||
|
|
||||||
|
@@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-fsm-ref }
|
||||||
|
|
||||||
|
Due to a limitation in Scala’s type inference, there is only the factory method
|
||||||
|
shown above, so you will probably write code like `TestFSMRef(new MyFSM)`
|
||||||
|
instead of the hypothetical `ActorRef`-inspired `TestFSMRef[MyFSM]`.
|
||||||
|
All methods shown above directly access the FSM state without any
|
||||||
|
synchronization; this is perfectly alright if the `CallingThreadDispatcher`
|
||||||
|
is used and no other threads are involved, but it may lead to surprises if you
|
||||||
|
were to actually exercise timer events, because those are executed on the
|
||||||
|
`Scheduler` thread.
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
|
### Testing the Actor's Behavior
|
||||||
|
|
||||||
|
When the dispatcher invokes the processing behavior of an actor on a message,
|
||||||
|
it actually calls `apply` on the current behavior registered for the
|
||||||
|
actor. This starts out with the return value of the declared `receive`
|
||||||
|
method, but it may also be changed using `become` and `unbecome` in
|
||||||
|
response to external messages. All of this contributes to the overall actor
|
||||||
|
behavior and it does not lend itself to easy testing on the `Actor`
|
||||||
|
itself. Therefore the `TestActorRef` offers a different mode of
|
||||||
|
operation to complement the `Actor` testing: it supports all operations
|
||||||
|
also valid on normal `ActorRef`. Messages sent to the actor are
|
||||||
|
processed synchronously on the current thread and answers may be sent back as
|
||||||
|
usual. This trick is made possible by the `CallingThreadDispatcher`
|
||||||
|
described below (see [CallingThreadDispatcher](#callingthreaddispatcher)); this dispatcher is set
|
||||||
|
implicitly for any actor instantiated into a `TestActorRef`.
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-behavior }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-behavior }
|
||||||
|
|
||||||
|
As the `TestActorRef` is a subclass of `LocalActorRef` with a few
|
||||||
|
special extras, also aspects like supervision and restarting work properly, but
|
||||||
|
beware that execution is only strictly synchronous as long as all actors
|
||||||
|
involved use the `CallingThreadDispatcher`. As soon as you add elements
|
||||||
|
which include more sophisticated scheduling you leave the realm of unit testing
|
||||||
|
as you then need to think about asynchronicity again (in most cases the problem
|
||||||
|
will be to wait until the desired effect had a chance to happen).
|
||||||
|
|
||||||
|
One more special aspect which is overridden for single-threaded tests is the
|
||||||
|
`receiveTimeout`, as including that would entail asynchronous queuing of
|
||||||
|
`ReceiveTimeout` messages, violating the synchronous contract.
|
||||||
|
|
||||||
|
@@@ note
|
||||||
|
|
||||||
|
To summarize: `TestActorRef` overwrites two fields: it sets the
|
||||||
|
dispatcher to `CallingThreadDispatcher.global` and it sets the
|
||||||
|
`receiveTimeout` to None.
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
|
### The Way In-Between: Expecting Exceptions
|
||||||
|
|
||||||
|
If you want to test the actor behavior, including hotswapping, but without
|
||||||
|
involving a dispatcher and without having the `TestActorRef` swallow
|
||||||
|
any thrown exceptions, then there is another mode available for you: just use
|
||||||
|
the `receive` method on `TestActorRef`, which will be forwarded to the
|
||||||
|
underlying actor:
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [TestkitDocSpec.scala]($code$/scala/docs/testkit/TestkitDocSpec.scala) { #test-expecting-exceptions }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [TestKitDocTest.java]($code$/java/jdocs/testkit/TestKitDocTest.java) { #test-expecting-exceptions }
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
You may of course mix and match both modi operandi of `TestActorRef` as
|
||||||
|
suits your test needs:
|
||||||
|
|
||||||
|
* one common use case is setting up the actor into a specific internal state
|
||||||
|
before sending the test message
|
||||||
|
* another is to verify correct internal state transitions after having sent
|
||||||
|
the test message
|
||||||
|
|
||||||
|
Feel free to experiment with the possibilities, and if you find useful
|
||||||
|
patterns, don't hesitate to let the Akka forums know about them! Who knows,
|
||||||
|
common operations might even be worked into nice DSLs.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# TestKit Example
|
|
||||||
|
|
||||||
Ray Roestenburg's example code from [his blog](http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html) adapted to work with Akka 2.x.
|
|
||||||
|
|
||||||
@@snip [TestKitUsageSpec.scala]($code$/scala/docs/testkit/TestKitUsageSpec.scala) { #testkit-usage }
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue