Finnfalter
Finnfalter

Reputation: 732

Akka actors' state not monitored properly

Monitoring Akka actors' state is said to be possible by using .underlyingActor.

In the example below, there is an ActorWithState using an Integer counter. The process of counter's incrementation and decrementation is tested by an ActorWithStateTest. The incrementation step always passes. However, counter's decrementation does not seem to work in that the 2nd assert always fails. The error message is the following. What is wrong?

[Error message of this test]

[info] ActorWithStateTest
[info] - should validate counter incrementation and decrementation *** FAILED ***
[info]   1 did not equal 0, Expected counter to be 0 after 'Terminated' message

[Actor with counter]

        object ActorWithState {
          case class Inc;
        }

        class ActorWithState(snooper: ActorRef) extends Actor with ActorLogging {
           var counter = 0

           def receive = {
               case Inc             => counter += 1 
               case Terminated(ref) => counter -= 1
           }
        }

[ActorTest shall test counter's behaviour]

class ActorWithStateTest extends TestKit(ActorSystem("SimpleTestSpec")) {

  "Test" should {
    "validate counter incrementation and decrementation" in {

      val aws = TestActorRef(Props(classOf[ActorWithState], testActor), name = "aws")
      val awsA: ActorWithState = aws.underlyingActor

      // tell aws to increment its counter
      aws ! ActorWithState.Inc

      // this assert always passes
      assert(awsA.counter == 1, ", Expected counter to be 1 after 'Inc' message")

      // triggers a 'Terminated' message being sent to aws
      val tp = TestProbe()
      aws    watch tp.ref
      system stop  tp.ref

      // [EDIT] the following assert passes if some - time consuming - processing is added here

      // the following assert does NOT pass, WHY?          
      assert(awsA.counter == 0, ", Expected counter to be 0 after 'Terminated' message")
    }
  }

[akka-actor: 2.3.2, akka-testkit: 2.3.2, scalatest: 2.0]

Upvotes: 2

Views: 369

Answers (1)

cmbaxter
cmbaxter

Reputation: 35443

As mentioned in my comment, your issue stems from the stopping of an actor being async. Your assertion is happening before the Terminated message hits your actor that you are testing. One quick and dirty way to fix this is to introduce another probe that also listens for the terminated event and perform an assertion on that first. That will wait until the terminated is received by the second watcher which should mean that your actor has also received the terminated event and you can perform your assertion. This worked for me:

  val tp = TestProbe()
  val tp2 = TestProbe()
  aws    watch tp.ref
  tp2    watch tp.ref
  system stop  tp.ref
  tp2.expectTerminated(tp.ref)

  assert(awsA.counter == 0, ", Expected counter to be 0 after 'Terminated' message")

Upvotes: 3

Related Questions