Testing akka actor termination

When testing akka code, I want to make sure a particular actor gets shut down within a time limit. I used to do it like this:


That isTerminated method is deprecated since Akka 2.2, and good thing too, since my test was wasting everyone’s time. Today I’m doing this instead:

import akka.testkit.TestProbe

val probe = new TestProbe(actorSystem)
probe.expectMsgPF(2.seconds){ case Terminated(actorRef) => true }

This says: set up a TestProbe actor, and have it watch the actorRef of interest. Wait for the TestProbe to receive notification that the actor of interest has been terminated. If actorRef has already terminated, that message will come right away. My test doesn’t have to wait the maximum allowed time.[1]

This works in any old test method with access to the actorSystem — I don’t have to extend akka.testkit.TestKit to use the TestProbe.

BONUS: In a property-based test, I don’t want to throw an exception, but rather return a result, a property with a nice label. In that case my function gets a little weirder:

def shutsDown(actorSystem: ActorSystem, 
              actorRef: ActorRef): Prop = {
  val maxWait = 2.seconds
  val probe = new TestProbe(actorSystem)
  try {
   probe.expectMsgPF(maxWait){case Terminated(actorRef) => true }
  } catch { 
   case ae: AssertionError => 
    false 😐 s”actor not terminated within $maxWait

[1] This is still blocking the thread until the Terminated message is received or the timeout expires. I eagerly await the day when test methods can return a Future[TestResult].

A suggestion for testing style in Clojure

Getting a test to fail is one thing; getting it to express why it failed is another.
Clojure.test provides an assertion macro: is

(deftest “my-function” 
  (testing “some requirement”
    (is (something-that-evaluates-to-bool (arg1) (arg2)) 
    “Message to print when it’s false”)))

When this assertion fails, in addition to the message, “expected” and “actual” results print. The is macro tries to be a little clever.

If the expression passed to is an S-expr, and the first element of the is recognized as a function. Then is prints that first symbol directly, then evaluates all the arguments to the function and prints the results. For instance:

expected: (function-name (arg1) (arg2))
actual: (not (function-name “1st arg value” “2nd arg value”))

However, if is does not recognize that first element as a function, the whole expression passed to is is evaluated for the actual, and you get:

expected: (something-that-evaluates-to-bool (arg1) (arg2))
actual: false

To get the more communicative output, write a function with a name that describes the property you’re testing. A top-level defn will be recognized by is as a function, so declare your test function that way.

For instance, if I’m testing whether a vector of numbers is sorted, I could use built-in functions:

(let [v [1 3 2 4]]
(is (apply <= v) “not sorted”))

Then I see[1]:

expected: (apply <= v)
actual: (not
            [1 3 2 4]))

Printing a function is never pretty. If, instead, I give the desired property a name:

(defn ascending? [v] (apply <= v))

(let [v [1 3 2 4]]
  (is (ascending? v) “not sorted”))

Then I get something much more communicative:

expected: (ascending? v)
actual: (not (ascending? [1 3 2 4]))

There it is — declarative test output through creation of simple functions.

Bonus: If you want to make your failure output even better, define special functionality just for your function, as @pjstadig did for =.

[1] I’m using humane-test-output to get the “actual” pretty-printed.