Merge branch 'wip-1952-testkit-java-∂π'
This commit is contained in:
commit
cde7b29a33
18 changed files with 1586 additions and 82 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-----------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
=========================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue