Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing timer events in akka

I have a function which uses scheduleOnce to schedule an event to happen some time in the future, and I want to write a test that checks that:

  • the event was indeed scheduled
  • it was scheduled at the correct time
  • the system behaves as expected when that event eventually triggers

but I don't want the test to actually wait several minutes doing nothing.

How should I best test code that uses akka's Scheduler?

like image 386
nornagon Avatar asked Nov 13 '14 23:11

nornagon


1 Answers

Here is an example of mocking out the scheduler as described by @lmm. In this example, we really test the full scheduling and handling of the action as two separate scenarios. The first testing that given some condition (a message of a certain type being received in my example) we will schedule a callback, and the second being the handling of the message that gets fired back to self when that timer goes off. The code is as follows:

object TimerExampleActor{
  case object ClearState
  case class ScheduleStateClearing(duration:FiniteDuration)
}

class TimerExampleActor extends Actor{
  import TimerExampleActor._

  var state:List[Int] = Nil

  def receive = {
    case ScheduleStateClearing(d) =>
      scheduler.scheduleOnce(d, self, ClearState)(context.dispatcher)

    case ClearState => 
      state = Nil
  }

  def scheduler = context.system.scheduler
}

Then, using specs2 and mockito, my test case is as follows:

class TimerExampleActorTest extends Specification with Mockito with NoTimeConversions{
  import TimerExampleActor._
  implicit val system = ActorSystem("test")

  trait testscope extends Scope{
    val mockScheduler = mock[Scheduler]
    val actor = TestActorRef(new TimerExampleActor{
      override def scheduler = mockScheduler 
    })
  }

  "A request to schedule state clearing" should{
    "schedule a callback for the message ClearState to self with the supplied duration" in new testscope{
      val dur = 1.minute
      actor ! ScheduleStateClearing(dur) 
      there was one(mockScheduler).scheduleOnce(dur, actor, ClearState)(actor.underlyingActor.context.dispatcher)      
    }
  }

  "A ClearState message received by the actor" should{
    "clear the interval 'state' List" in new testscope{
      actor.underlyingActor.state = List(1,2,3)
      actor ! ClearState
      actor.underlyingActor.state mustEqual Nil
    }
  }
}

You can see that when I create the actor instance under test I override the method I created to get me the instance of the scheduler, allowing me to return a mock. This is not the only way to go about testing something like this, but it certainly can be one option for you to consider.

like image 157
cmbaxter Avatar answered Oct 03 '22 21:10

cmbaxter