user_1357
user_1357

Reputation: 7940

Akka testing supervisor error handling

I have following:

class Supervisor(dataProvider: DatabaseClientProvider) extends Actor {
  val writer = context.actorOf(Props(classOf[Child], dataProvider.get))
  def receive: Receive = {
    case Msg => writer forward msg 
  }
  override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 100) {
    case e: ConnectionException => Resume
  }
}

class Child(db: DatabaseClient) extends Actor {
  def receive: Receive = {
    case msg:Msg => db.write(text)
  }
}

I want to unit test above code basically I am trying to make sure when the exception occurs, we are still resuming the processing as you can see below. The problem is that no exception is caught by the supervisor. What is the best way to test the code below?

"resume handling messages when exception occurs" in {

  // Given
  val msg1 = Msg("Some msg1")
  val msg2 = Msg("Some msg2")

  //Throw an exception when attempting to write msg1
  val databaseClient = mock[DatabaseClient]
  when(databaseClient.write(msg1.text).thenThrow(ConnectionException("Error!"))

  val dataProvider = mock[DatabaseClientProvider]
  when(dataProvider.get).thenReturn(databaseClient)

  val supervisor = system.actorOf(Props(new Supervisor(dataProvider)))

  // When
  intercept[ConnectionException] {
      supervisor ! msg1
  }

  // When
  supervisor ! msg2

  // Then
  verify(databaseClient.write("Some msg"), times(2))   
}

Upvotes: 8

Views: 2024

Answers (2)

Jake Greene
Jake Greene

Reputation: 5618

To test the supervisor's behaviour when a child throws an exception you must test the supervisorStrategy. Using a TestActorRef, you can get access to the supervisorStrategy's partial function and assert that a given Exception results in the expected Directive

val supervisor = TestActorRef[Supervisor](Props(new Supervisor(dataProvider)))
val strategy = supervisor.underlyingActor.supervisorStrategy.decider
strategy(ConnectionException("boom")) should be (Resume)

Upvotes: 13

Denis Makarenko
Denis Makarenko

Reputation: 2938

I bet the problem is in this method:

  def receive: Receive = {
    case Msg => writer forward Msg 
  }

"case Msg" is triggered by typeclass Msg, not by an instance of class Msg. Something like this should work:

  def receive: Receive = {
    case msg:Msg => writer forward msg 
  }

Upvotes: 0

Related Questions