CPS
CPS

Reputation: 537

How do I unit test an akka Actor synchronously?

I've got an actor that will write a file after receiving a certain number of messages.

When compiling, Maven runs all the unit tests we have defined. The problem is that the unit test to check that the file was successfully written to disk happens before the Actor writes the file (which it does correctly).

I've found some documentation for testing Actors, but they're all out of date by several years. How can I wait for a bit before checking that the file exists?

Upvotes: 5

Views: 5237

Answers (2)

torbinsky
torbinsky

Reputation: 1468

For synchronous actor testing, use TestProbes (akka.testkit) similar to this:

import org.scalatest.{FunSuite, BeforeAndAfterAll}
import org.scalatest.matchers.ShouldMatchers
import scala.concurrent.duration._
import scala.util.Random
import scala.util.control.NonFatal
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import akka.actor.{ActorSystem, ActorRef, Actor, Props}
import akka.testkit.{TestKit, ImplicitSender, TestProbe}
import scala.concurrent._
import ExecutionContext.Implicits.global

@RunWith(classOf[JUnitRunner])
class Example extends TestKit(ActorSystem("filetest"))
  with FunSuite
  with BeforeAndAfterAll
  with ShouldMatchers
  with ImplicitSender
  with Tools {

  override def afterAll(): Unit = {
    system.shutdown()
  }

  test("Show synchronous actor test") {
    val tester = TestProbe()
    val fileActor = system.actorOf(Props(classOf[FileActor]), "case2-primary")

    tester.send(fileActor, CreateFile(1))
    tester.expectMsg(FileCreated(1)) // Will synchronously wait (~500ms in this example)
  }
}

case class CreateFile(id: Long)
case class FileCreated(id: Long)

class FileActor extends Actor {
  def receive = {
    case CreateFile(id) => {
      val s = sender
      createFile(id).map( id => s ! FileCreated(id))
    }
  }

  def createFile(id: Long): Future[Long] = future {
    // "Create" the file...
    Thread.sleep(500)
    id
  }
}

Upvotes: 2

yǝsʞǝla
yǝsʞǝla

Reputation: 16422

Just to sum up all comments and suggestions:

  • Refactor non-actor specific code to regular classes and test it using regular means (unit tests, etc). This is more of a unit test approach.
  • Use TestActorRef to test a single actor - you can control events synchronously and rich into actor internals. Use TestKit which provides much more including "special" actor system. This is more of a behavioural test approach.
  • Use documentation here to learn more about how Akka TestKit works but in short you get control over your actor and synchronous execution.

Upvotes: 5

Related Questions