Akka: How to ensure that message has been received?

233 views Asked by At

I have an actor Dispenser. What it does is it

  1. dispenses some objects by request
  2. listens to arriving new ones

Code follows

class Dispenser extends Actor {
    override def receive: Receive = {
        case Get =>
            context.sender ! getObj()
        case x: SomeType =>
            addObj(x)
    }
}

In real processing it doesn't matter whether 1 ms or even few seconds passed since new object was sent until the dispenser starts to dispense it, so there's no code tracking it.

But now I'm writing test for the dispenser and I want to be sure that firstly it receives new object and only then it receives a Get request.

Here's the test code I came up with:

val dispenser = system.actorOf(Props.create(classOf[Dispenser]))
dispenser ! obj
Thread.sleep(100)
val task = dispenser ? Get()
val result = Await.result(task, timeout)
check(result)

It satisfies one important requirement - it doesn't change original code. But it is

  1. At least 100ms seconds slow even on very high performance boxes
  2. Unstable and fails sometimes because 100 ms or any other constant doesn't provide any guaranties.

And the question is how to make a test that satisfies requirement and doesn't have cons above (neither any other obvious cons)

2

There are 2 answers

5
tariksbl On

You can take out the Thread.sleep(..) and your test will be fine. Akka guarantees the ordering you need.

With the code

dispenser ! obj
val task = dispenser ? Get()

dispenser will process obj before Get deterministically because

  1. The same thread puts obj then Get in the actor's mailbox, so they're in the correct order in the actor's mailbox
  2. Actors process messages sequentially and one-at-a-time, so the two messages will be received by the actor and processed in the order they're queued in the mailbox.

(..if there's nothing else going on that's not in your sample code - routers, async processing in getObj or addObj, stashing, ..)

0
stanislav.chetvertkov On

Akka FSM module is really handy for testing underlying state and behavior of the actor and does not require to change its implementation specifically for tests. By using TestFSMRef one can get actors current state and and data by:

val testActor = TestFSMRef(<actors constructor or Props>)
testActor.stateName shouldBe <state name>
testActor.stateData shouldBe <state data>

http://doc.akka.io/docs/akka/2.4.1/scala/fsm.html