Merge branch 'wip-1952-testkit-java-∂π'

This commit is contained in:
Roland 2012-07-04 17:38:20 +02:00
commit cde7b29a33
18 changed files with 1586 additions and 82 deletions

View file

@ -125,7 +125,10 @@ class TestkitDocSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
val actorRef = TestActorRef(new MyActor)
// hypothetical message stimulating a '42' answer
val result = Await.result((actorRef ? Say42), 5 seconds).asInstanceOf[Int]
val future = actorRef ? Say42
val result = future.value.get match {
case Right(x: Int) x
}
result must be(42)
//#test-behavior
}
@ -146,7 +149,7 @@ class TestkitDocSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
val actorRef = TestActorRef(new Actor {
def receive = {
case boom throw new IllegalArgumentException("boom")
case "hello" throw new IllegalArgumentException("boom")
}
})
intercept[IllegalArgumentException] { actorRef.receive("hello") }
@ -272,4 +275,15 @@ class TestkitDocSpec extends AkkaSpec with DefaultTimeout with ImplicitSender {
//#test-kit-base
}
"demonstrate within() nesting" in {
intercept[AssertionError] {
//#test-within-probe
val probe = TestProbe()
within(1 second) {
probe.expectMsg("hello")
}
//#test-within-probe
}
}
}

View file

@ -93,7 +93,7 @@ There are 4 different types of message dispatchers:
* CallingThreadDispatcher
- This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads,
but it can be used from different threads concurrently for the same actor. See :ref:`TestCallingThreadDispatcherRef`
but it can be used from different threads concurrently for the same actor. See :ref:`Scala-CallingThreadDispatcher`
for details and restrictions.
- Sharability: Unlimited

View file

@ -424,7 +424,7 @@ This FSM will log at DEBUG level:
* all state transitions
Life cycle changes and special messages can be logged as described for
:ref:`Actors <actor.logging>`.
:ref:`Actors <actor.logging-scala>`.
Rolling Event Log
-----------------

View file

@ -67,15 +67,6 @@ Since :class:`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.
Expecting Exceptions
--------------------
Testing that an expected exception is thrown while processing a message sent to
the actor under test can be done by using a :class:`TestActorRef` :meth:`receive` based
invocation:
.. includecode:: code/docs/testkit/TestkitDocSpec.scala#test-expecting-exceptions
.. _TestFSMRef:
Testing Finite State Machines
@ -111,8 +102,8 @@ operation to complement the :class:`Actor` testing: it supports all operations
also valid on normal :class:`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 :class:`CallingThreadDispatcher`
described below; this dispatcher is set implicitly for any actor instantiated
into a :class:`TestActorRef`.
described below (see `CallingThreadDispatcher`_); this dispatcher is set
implicitly for any actor instantiated into a :class:`TestActorRef`.
.. includecode:: code/docs/testkit/TestkitDocSpec.scala#test-behavior
@ -134,8 +125,8 @@ One more special aspect which is overridden for single-threaded tests is the
dispatcher to :obj:`CallingThreadDispatcher.global` and it sets the
:obj:`receiveTimeout` to None.
The Way In-Between
------------------
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 :class:`TestActorRef` swallow
@ -143,10 +134,7 @@ any thrown exceptions, then there is another mode available for you: just use
the :meth:`receive` method :class:`TestActorRef`, which will be forwarded to the
underlying actor:
.. includecode:: code/docs/testkit/TestkitDocSpec.scala#test-unhandled
The above sample assumes the default behavior for unhandled messages, i.e.
that the actor doesn't swallow all messages and doesn't override :meth:`unhandled`.
.. includecode:: code/docs/testkit/TestkitDocSpec.scala#test-expecting-exceptions
Use Cases
---------
@ -205,12 +193,12 @@ Built-In Assertions
The above mentioned :meth:`expectMsg` is not the only method for formulating
assertions concerning received messages. Here is the full list:
* :meth:`expectMsg[T](d: Duration, msg: T): T`
* :meth:`expectMsg[T](d: Duration, msg: T): T`
The given message object must be received within the specified time; the
object will be returned.
* :meth:`expectMsgPF[T](d: Duration)(pf: PartialFunction[Any, T]): T`
* :meth:`expectMsgPF[T](d: Duration)(pf: PartialFunction[Any, T]): T`
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
@ -219,40 +207,40 @@ assertions concerning received messages. Here is the full list:
the deadline from the innermost enclosing :ref:`within <TestKit.within>`
block instead.
* :meth:`expectMsgClass[T](d: Duration, c: Class[T]): T`
* :meth:`expectMsgClass[T](d: Duration, c: Class[T]): T`
An object which is an instance of the given :class:`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, have a look at
:meth:`expectMsgAllClassOf` with a single given class argument.
* :meth:`expectMsgType[T: Manifest](d: Duration)`
* :meth:`expectMsgType[T: Manifest](d: Duration)`
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
method is approximately equivalent to
``expectMsgClass(manifest[T].erasure)``.
* :meth:`expectMsgAnyOf[T](d: Duration, obj: T*): T`
* :meth:`expectMsgAnyOf[T](d: Duration, obj: T*): T`
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
received object will be returned.
* :meth:`expectMsgAnyClassOf[T](d: Duration, obj: Class[_ <: T]*): T`
* :meth:`expectMsgAnyClassOf[T](d: Duration, obj: Class[_ <: T]*): T`
An object must be received within the given time, and it must be an
instance of at least one of the supplied :class:`Class` objects; the
received object will be returned.
* :meth:`expectMsgAllOf[T](d: Duration, obj: T*): Seq[T]`
* :meth:`expectMsgAllOf[T](d: Duration, obj: T*): Seq[T]`
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 (compared with
``==``) it. The full sequence of received objects is returned.
* :meth:`expectMsgAllClassOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
* :meth:`expectMsgAllClassOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
A number of objects matching the size of the supplied :class:`Class` array
must be received within the given time, and for each of the given classes
@ -260,25 +248,25 @@ assertions concerning received messages. Here is the full list:
(compared with ``==``) it (this is *not* a conformance check). The full
sequence of received objects is returned.
* :meth:`expectMsgAllConformingOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
* :meth:`expectMsgAllConformingOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
A number of objects matching the size of the supplied :class:`Class` array
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
instance of this class. The full sequence of received objects is returned.
* :meth:`expectNoMsg(d: Duration)`
* :meth:`expectNoMsg(d: Duration)`
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.
* :meth:`receiveN(n: Int, d: Duration): Seq[AnyRef]`
* :meth:`receiveN(n: Int, d: Duration): Seq[AnyRef]`
``n`` messages must be received within the given time; the received
messages are returned.
* :meth:`fishForMessage(max: Duration, hint: String)(pf: PartialFunction[Any, Boolean]): Any`
* :meth:`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
function matches and returns ``false``. Returns the message received for
@ -288,13 +276,13 @@ assertions concerning received messages. Here is the full list:
In addition to message reception assertions there are also methods which help
with message flows:
* :meth:`receiveOne(d: Duration): AnyRef`
* :meth:`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
call is non-blocking (polling mode).
* :meth:`receiveWhile[T](max: Duration, idle: Duration, messages: Int)(pf: PartialFunction[Any, T]): Seq[T]`
* :meth:`receiveWhile[T](max: Duration, idle: Duration, messages: Int)(pf: PartialFunction[Any, T]): Seq[T]`
Collect messages as long as
@ -309,14 +297,14 @@ with message flows:
idle timeout feature). The number of expected messages defaults to
``Int.MaxValue``, which effectively disables this limit.
* :meth:`awaitCond(p: => Boolean, max: Duration, interval: Duration)`
* :meth:`awaitCond(p: => Boolean, max: Duration, interval: Duration)`
Poll the given condition every :obj:`interval` until it returns ``true`` or
the :obj:`max` duration is used up. The interval defaults to 100 ms and the
maximum defaults to the time remaining in the innermost enclosing
:ref:`within <TestKit.within>` block.
* :meth:`ignoreMsg(pf: PartialFunction[AnyRef, Boolean])`
* :meth:`ignoreMsg(pf: PartialFunction[AnyRef, Boolean])`
:meth:`ignoreNoMsg`
@ -329,8 +317,8 @@ with message flows:
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.
Expecting Exceptions
--------------------
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.
@ -341,6 +329,20 @@ exceptions:
.. includecode:: code/docs/testkit/TestkitDocSpec.scala#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 event handler with the
:class:`TestEventListener` in your ``application.conf`` to enable this
function::
akka.event-handlers = [akka.testkit.TestEventListener]
.. _TestKit.within:
Timing Assertions
@ -363,7 +365,7 @@ The block given to :meth:`within` must complete after a :ref:`Duration` which
is between :obj:`min` and :obj:`max`, where the former defaults to zero. The
deadline calculated by adding the :obj:`max` parameter to the block's start
time is implicitly available within the block to all examination methods, if
you do not specify it, is is inherited from the innermost enclosing
you do not specify it, it is inherited from the innermost enclosing
:meth:`within` block.
It should be noted that if the last message-receiving assertion of the block is
@ -473,8 +475,9 @@ B``, as long as a certain protocol is obeyed.
.. includecode:: ../../akka-testkit/src/test/scala/akka/testkit/TestProbeSpec.scala#autopilot
The :meth:`run` method must return the auto-pilot for the next message, wrapped
in an :class:`Option`; setting it to :obj:`None` terminates the auto-pilot.
The :meth:`run` method must return the auto-pilot for the next message, which
may be :class:`KeepRunning` to retain the current one or :class:`NoAutoPilot`
to switch it off.
Caution about Timing Assertions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -483,23 +486,13 @@ The behavior of :meth:`within` blocks when using test probes might be perceived
as counter-intuitive: you need to remember that the nicely scoped deadline as
described :ref:`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
:class:`TestKit` instance::
:class:`TestKit` instance:
class SomeTest extends TestKit(_system: ActorSystem) with ImplicitSender {
.. includecode:: code/docs/testkit/TestkitDocSpec.scala#test-within-probe
val probe = TestProbe()
Here, the ``expectMsg`` call will use the default timeout.
within(100 millis) {
probe.expectMsg("hallo") // Will hang forever!
}
}
This test will hang indefinitely, because the :meth:`expectMsg` call does not
see any deadline. Currently, the only option is to use ``probe.within`` in the
above code to make it work; later versions may include lexically scoped
deadlines using implicit arguments.
.. _TestCallingThreadDispatcherRef:
.. _Scala-CallingThreadDispatcher:
CallingThreadDispatcher
=======================
@ -598,7 +591,7 @@ has to offer:
exception stack traces
- Exclusion of certain classes of dead-lock scenarios
.. _actor.logging:
.. _actor.logging-scala:
Tracing Actor Invocations
=========================