如何正确使用Akka-TestKit TestProbe?

时间:2015-08-04 10:21:06

标签: scala akka actor akka-testkit

我从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)
  }
}

我认为这不是测试的正确或良好用法。 当我删除tester1.expectMsg(500.milliseconds, "Hello")测试失败时, 所以test2的测试依赖于测试tester1。在我看来这很糟糕。

同样将行context.system.scheduler.scheduleOnce(400.milliseconds, actorRef1, "Hello")(context.system.dispatcher)更改为100毫秒的延迟会导致测试失败。所以测试消息2取决于发送消息1.在我看来,这也很糟糕。

要解决这个问题,我会在发送消息后添加一个Thread.sleep,并将#expectMsg的等待时间更改为0毫秒。 Thread.sleep在测试中对我来说也不好看,但我认为它是演员测试中必须的。这是正确的方法吗?

我认为TestProbe是为测试多个actor而设计的。但对我来说,#expectMsg的等待时间参数在测试多个actor时非常无用。

欢迎任何评论。

2 个答案:

答案 0 :(得分:3)

您可以尝试使用within块,如下所示:

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

这样,如果您将断言注释为tester1,则测试仍会通过。

答案 1 :(得分:2)

测试探针同步工作。 expectMsg方法正在阻止。这很好,因为你想在一个线程中运行测试并控制它。这种方式没有竞争条件。

预期消息时的超时是从读取行的时间开始计算的,这意味着tester2.expectMsg(500.milliseconds, "Hello")中的500毫秒将在tester1收到消息后开始倒计时。

如果tester1没有收到消息,您希望立即通过测试,则无需继续tester2

另一方面,调度程序在多个线程中异步完成,倒计时开始立即,因为没有阻塞。因此,预定的400毫安和800毫升将同时开始倒计时(差不多)。

因此将400毫秒更改为100毫秒意味着第二条消息仍将从开始时大约800毫秒发送,但tester1将从预期开始100 + 500 = 600毫秒。这就是它失败的原因。