4

我从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")测试失败时,tester2 的测试取决于测试 tester1。在我看来这很糟糕。

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

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

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

欢迎任何意见。

4

2 回答 2

3

你可以尝试一个within块,而不是像这样:

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

这样,如果您注释掉tester1测试的断言仍然可以通过。

于 2015-08-04T13:51:16.373 回答
2

测试探针同步工作。该expectMsg方法是阻塞的。这很好,因为您想在一个线程中运行测试并对其进行控制。这种方式没有竞争条件。

等待消息的超时时间是从读取该行的时间开始计算的,这意味着只有在收到他的消息后,500 毫秒tester2.expectMsg(500.milliseconds, "Hello")才会开始倒计时。tester1

如果tester1没有收到消息,你想立即测试失败,你不需要继续tester2

另一方面,调度程序是在多个线程中异步完成的,并且由于没有阻塞,倒计时立即开始。因此,预定的 400 毫秒和 800 毫秒将同时(几乎)开始倒计时。

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

于 2015-08-04T12:51:09.807 回答