Search code examples
scalatestingakkaactor

Testing actor crash using TestActorRef


I am new to actors, I am learning, how to test actors using TestActorRef

My actor code:

package actors

import actors.GreetingActor.Hi
import akka.actor.Actor

object GreetingActor {
  case class Hi()
}

class GreetingActor extends Actor {

  var greeting = ""

  override def receive: Receive = {
    case Hi() =>
      greeting = "Hi"
    case _ =>
      throw new IllegalArgumentException("not supported message")
  }

  override def postRestart(reason: Throwable) = {
    println(s"actor is restarted because of ${reason.getMessage}")
  }
}

I am sure that everything works as I want in this code, but I can't show it in test. Especially I can't show that one of the most important thing, that my actor crashed. The test is very simple and obvious I send message that is not Hi() and should track somehow that actor crashed with IllegalArgumentException. My current test code:

package actors

import actors.GreetingActor.Hi
import akka.actor.ActorSystem
import akka.testkit.{TestActorRef, TestKit}
import org.scalatest.{MustMatchers, WordSpecLike}

class GreetingActorTest extends TestKit(ActorSystem("testsystem")) with WordSpecLike
  with MustMatchers with StopSystemAfterAll {

  "A Hello Actor" must {
    "change state when it receives a message, single threaded" in {
      val greetingActor = TestActorRef[GreetingActor]
      greetingActor ! Hi()
      greetingActor.underlyingActor.greeting mustBe "Hi"
    }
    "throw exception when it received unknown message, single threaded" in {
      val greetingActor = TestActorRef[GreetingActor]
      greetingActor ! "hi"
      //some code that checks that actor crashed
    }
  }

}

The question is how can I track in test that my actor crashed using TestActorRef? Appreciate any help.


Solution

  • Change your test to the following:

    "throw exception when it received unknown message, single threaded" in {
      assertThrows[IllegalArgumentException] {
          val greetingActor = TestActorRef[GreetingActor]
    
          greetingActor.receive("hi")
      }
    }
    

    Per the actor docs, you need to use receive so the exception doesn't get swallowed:

    http://doc.akka.io/docs/akka/current/scala/testing.html#The_Way_In-Between__Expecting_Exceptions