Saturday, February 12, 2011

Unit testing Akka Actors with the TestKit

Just a quick post to show some examples of how you can use the akka.util.TestKit to unit test Actors. I'm not sure when it was added to Akka but it is really handy. Instead of using CyclicBarriers and CountDownLatches, you just mixin this very handy trait into your spec, akka.util.TestKit.

I added some examples of test scenarios that pop up frequently when working with Actors:
  • You send something to an Actor and you would like to know if it received the message
  • You send something to an Actor and that in turn does some processing and on success sends something to another Actor and you would like to know if that Actor received a specific message
  • You send some messages and you are not interested in every message the Actor receives and would like to ignore them until some message is received
  • You would like to know for sure that an Actor does not send through certain messages to others
  • You would like to know what an Actor sends back as a response based on a certain message
Testing this by hand with the correct concurrent latches and barriers can be brittle and quite error prone, not to mention quite complex.

So how does it work? The TestKit sort of swaps out the senderOption of an Actor through an implicit value scoping trick, which means that when your Actor sends back a message (to "the sender", for instance with a reply), it automatically sends it back to a testActor in TestKit, which queues up messages for you so you can inspect stuff. You can also use that same testActor when you have an Actor that sends messages to some ActorRef. You just switch out the ActorRef you would normally pass to your Actor with the testActor Ref.

I added some very simple Actors for the above scenarios, which touch a lot of typical interactions:
  • EchoActor - an Actor that just echoes any message you send to it
  • ForwardingActor - an Actor that forwards a message to another Actor(Ref)
  • FilteringActor - an Actor that forwards only certain messages (of type String)
  • SequencingActor - an Actor that forwards some random amount of uninteresting messages, an interesting message, and then some random amount of uninteresting messages
As you can see in the code below, you use a within block to indicate that some piece of code must complete with a certain duration. Every within block gets handled sequentially to keep things simple. Some nice methods and implicits in akka.util.duration make it possible to just write 'within(100 millis)' to indicate a duration of 100 milliseconds, or 'within(50 millis, 100 millis)' to indicate a min and max for the duration.
Then you just bang out some messages on an ActorRef. After that you can use expectMsg to assert that a specific message has been received, or expectNoMsg to assert that no message should have been received.  'ignoreMsg' takes a partial function that ignores all messages for which the partial function returns true (Funny, the scala code explains it better than that sentence just now).  in the test with the SequencingActor, I ignore all messages that are not "something", than assert the "something" message and than ignore everything again that is not "something", making sure there are no messages after that. Make sure you stop all actors after all.
Anyway, enough rambling, check out this gist :)


4 comments:

  1. Glad you like it, I added it between 1.0-RC2 and 1.0-RC3.

    One addition: the testActor shuts itself down after a default idle timeout of 5 seconds, which can be changed by calling setTestActorTimeout. So, calling stopTestActor is not always mandatory. The timeout can be disabled by setting it to Duration.Inf.

    ReplyDelete
  2. This is a great article and a great facility. Is there any way to simulate a response from testActor? Thanks

    ReplyDelete
  3. Hi BFL,

    What is your use case? Anything you send to the testActor is what you can check with expectMsg. So you could simply use it as any actorRef. Does that help?

    ReplyDelete