How to use Akka-TestKit TestProbe?

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 this is a good or good use for a test. When I delete tester1.expectMsg(500.milliseconds, "Hello") , the test fails, so testing tester2 depends on test tester1. In my opinion, this is bad.

Also, changing the line context.system.scheduler.scheduleOnce(400.milliseconds, actorRef1, "Hello")(context.system.dispatcher) to a delay of 100 milliseconds, run the test. Therefore, test message 2 depends on sending message 1. In my opinion, this is also bad.

To solve this problem, I would add Thread.sleep after sending the message and change the wait time for #expectMsg to 0 milliseconds. Thread.sleep is also not suitable for me in tests, but I think it is necessary for testing the actor. Is it correct?

I thought TestProbe was designed to test multiple participants. But for me, the #expectMsg timeout parameter is completely useless when testing multiple participants.

Any comments are welcome.

+4
source share
2 answers

Instead, you can try the within block:

 within(800.milliseconds, 900.milliseconds){ tester1.expectMsg("Hello") tester2.expectMsg("Hello") } 

Thus, if you comment out the statement tester1 , the test will pass anyway.

+3
source

The test probe works synchronously. The expectMsg method blocks. And that’s good, because you want to run tests on a single thread and control it. Thus, there are no race conditions.

Waiting time for waiting messages is measured from the moment the line was read, which means that 500 millions in tester2.expectMsg(500.milliseconds, "Hello") will start counting only after tester1 receives its message.

If tester1 does not receive the message, you should skip the test immediately, you do not need to continue tester2 .

On the other hand, the scheduler runs asynchronously, in multiple threads, and the countdown starts immediately , because the lock does not exist. Thus, the planned 400 milliseconds and 800 million will start counting (almost) at the same time.

Thus, changing 400 milliseconds to 100 milliseconds means that the second message will still be sent approximately 800 milliseconds from the beginning, but tester1 will wait 100 + 500 = 600 milliseconds from the beginning. That is why he fails.

+2
source

All Articles