0

I'm having a strange behaviour when using context.become in a PersistentActor (Not sure if the persistence has anything to do with the cause of the problem). My code is something like:

class MyActor extends PersistentActor {

    import context._

    // Messages
    case object Start
    case object Ready
    case object MessageX

    // Events
    case object Started

    def recieveRecover: Receive = { /* Not relevant, I think */}

    def receiveCommand: Receive = idle

    def idle: Receive = {
        case Start => 
            persist(Started) { _ =>
                sender() ! Ready
                become(ready)
            }
    }

    def ready: Receive = {
        case MessageX => doSomething()
    }
}

And I have two tests in the same MyActorSpec file. The first one simply tests the 'idle' state and the second one tests the 'ready' state:

"Test A" in {
    val actorRef = system.actorOf( MyActor.props(), "test-A-actor" )

    actorRef ! Start

    expectMsg(Ready)
}

"Test B" in {
    val actorRef = system.actorOf( MyActor.props(), "test-B-actor" )

    actorRef ! Start

    expectMsg(Ready) /* It fails here because for some reason the actorRef 
    was created with its 'receiveCommand' block equal to 
    the 'ready' block and not equal to the 'idle' block as its suppossed to. 
    So it timeouts here waiting for the message which is not handled in
    the 'ready' block */

    actorRef ! MessageX

    testSomethingAboutTheMessageX() 
}

If I run both tests, the first one succeeds but the second one fails waiting for the ready message (as explained in a comment in the second test). If I run only the second test it passes. So, I'm not sure if I did something wrong when defining the actor.

UPDATE: I tried removing the persistence for the Started event (the persist(Started) part) as suggested and the tests worked as expected (Both actors were created in the idle state). So, what's happenning is that the events on the first actor instance are being persisted and then the second actor instance is replaying them, and this is because the actor instances are being created with the same persistenceId (DOH!). So, the way to make the tests independent was simply to instantiate each actor with a diferent persistenceId.

miguel
  • 714
  • 7
  • 17
  • The fact that the result changes depending on which tests are run suggests that your tests are not independent. Perhaps try creating a new `system` within each test to ensure there is no shared state? – millhouse Nov 02 '14 at 23:10

2 Answers2

2

What's happenning is that the events on the first actor instance are being persisted and then the second actor instance is replaying them, and this is because the actor instances are being created with the same persistenceId (DOH!). So, the way to make the tests independent is simply to instantiate each actor with a diferent persistenceId.

miguel
  • 714
  • 7
  • 17
0

It may be caused by Specs2 running all the tests concurrently because its 'sequential' argument has default value 'false'.

To force all the tests execute one after another, specify sequential at the top of specification, something like this:

class MyActorSpec extends Specification {

  // Run the tests sequentially
  sequential


  "MyActor" should {
    "Test A" in {
          // test goes here
    }   
    "Test B" in {
          // test goes here
    }   
  }
}

Take a look at http://etorreborre.github.io/specs2/guide/org.specs2.guide.Runners.html for details.

Denis Makarenko
  • 2,853
  • 15
  • 29
  • hmmm no I don't think this is it. I'm using scalatest and made the tests run sequentially and I got the same behaviour. I think thats not the problem because even if they are running in parallel they use different actor instances, so there should be no overlap. – miguel Nov 03 '14 at 21:32
  • That's true but both tests share the same actor system and the same implicit sender actor is used to receive response from your actors in expectMsg(). So you may have a race condition when Ready response sent by actor B is received in Test A – Denis Makarenko Nov 03 '14 at 21:38
  • I tested adding a `case Start => println("Started in ready!")` clause to the `ready`recieve block and the second test prints it. So the problem is not the first test recieving the `Ready` message and thus causing the timeout in the second test but instead the second test creates the actor having its recieve block set to `ready` and not `idle`. – miguel Nov 03 '14 at 23:10
  • Hmm, I doubt that the problem is in become() but you can easily test whether the actor is in 'ready' state when it receives Start message by logging all unrecognized messages like this: def ready: Receive = { case MessageX => doSomething() case m : Any => println(s"Got a message in ready state : $m") } – Denis Makarenko Nov 03 '14 at 23:52
  • Also, try to temporarily comment out the "persist(Started) { _ =>" line (and closing bracket, too) to rule out that it affects sending the Ready message. – Denis Makarenko Nov 03 '14 at 23:56
  • Another idea is set breakpoint on become(ready) and see whether it hit twice. – Denis Makarenko Nov 03 '14 at 23:57
  • I haven't used PersistentActor, but if the callback passed to persist() is called acynchronously, sender() may return an incorrect value. I.e. not the sender for the original message but rather the sender for last processed message which is not necessarily the same. So response may go to incorrect destination. I doubt that it is happening in your tests but this is something to consider when running it in non-test environment. – Denis Makarenko Nov 04 '14 at 00:00
  • thanks, I tried removing the persistence and I think I got it. I think `persist()` runs synchonously (there is a `persistAsync()` but that's not what I use), so that wasn't the problem. – miguel Nov 04 '14 at 01:42