Move 'synchronous testing' to the bottom (#22947) (#22973)

* Unify scala/java versions of the testing docs
* Move 'synchronous testing' to the bottom (#22947)
This commit is contained in:
Arnout Engelen 2017-05-22 03:16:02 -07:00 committed by GitHub
parent 00e7917b9d
commit 037252b2a3
5 changed files with 412 additions and 973 deletions

View file

@ -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

View file

@ -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 actors 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 Javas 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 forwarders 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 guardians thread instead of the callers
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 threads interrupted status
will be set again.
If during message processing an `InterruptedException` is thrown then it
will be caught inside the CallingThreadDispatchers message handling loop, the
threads 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 systems 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).

View file

@ -0,0 +1 @@
../scala/testing.md

View file

@ -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

View file

@ -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 Scalas 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 actors 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 forwarders 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 actors `receive` function: statement to be applied to an actors `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
Akkas own test suite is written using [ScalaTest](http://scalatest.org), Akkas 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 Scalas 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.

View file

@ -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 }