I expanded the example from http://doc.akka.io/docs/akka/snapshot/scala/testing.html#Using_Multiple_Probe_Actors.
import akka.actor._
import akka.testkit.{TestProbe, TestKit}
import org.scalatest.{Suites, BeforeAndAfter, BeforeAndAfterAll, FlatSpecLike}
import scala.concurrent.duration._
class TestProbesTestSuites extends Suites(new TestProbesTest)
class TestProbesTest extends TestKit(ActorSystem("TestProbesTestSystem")) with FlatSpecLike with BeforeAndAfterAll with BeforeAndAfter {
override def afterAll: Unit = {
TestKit.shutdownActorSystem(system)
}
"A TestProbeTest" should "test TestProbes" in {
val actorRef = system.actorOf(Props[TestActor], "TestActor")
val tester1 = TestProbe()
val tester2 = TestProbe()
Thread.sleep(500.milliseconds.toMillis)
actorRef ! (tester1.ref, tester2.ref)
// When you comment the next line the test fails
tester1.expectMsg(500.milliseconds, "Hello")
tester2.expectMsg(500.milliseconds, "Hello")
// Alternative test
// Thread.sleep(500.milliseconds.toMillis)
// tester1.expectMsg(0.milliseconds, "Hello")
// tester2.expectMsg(0.milliseconds, "Hello")
()
}
}
class TestActor extends Actor with ActorLogging {
override def receive: Receive = {
case (actorRef1: ActorRef, actorRef2: ActorRef) => {
// When you change the schedule time in the next line to 100.milliseconds the test fails
context.system.scheduler.scheduleOnce(400.milliseconds, actorRef1, "Hello")(context.system.dispatcher)
context.system.scheduler.scheduleOnce(800.milliseconds, actorRef2, "Hello")(context.system.dispatcher)
}
case x => log.warning(x.toString)
}
}
I do not think that this a correct or good usage for a test.
When I remove the tester1.expectMsg(500.milliseconds, "Hello")
the test fails,
so testing of tester2 dependes on testing tester1. In my opinion this bad.
Also changing line context.system.scheduler.scheduleOnce(400.milliseconds, actorRef1, "Hello")(context.system.dispatcher)
to a delay of 100 milliseconds let the test fail. So testing message 2 depends on sending message 1. In my opinion this is bad, too.
To solve this I would add a Thread.sleep after sending the message and change the wait time for both #expectMsg to 0 milliseconds. Thread.sleep does also not look good for me in tests, but I think it is a must in actor testing. Is this the right way?
I thought TestProbe is made for testing multiple actors. But for me the wait time parameter of #expectMsg is quite useless, when testing multiple actors.
Any remarks are welcome.
You could try a within
block instead like so:
within(800.milliseconds, 900.milliseconds){
tester1.expectMsg("Hello")
tester2.expectMsg("Hello")
}
That way if you comment out the assertion to tester1
the test still passes.