如何正确使用Akka-TestKit TestProbe?
How to use Akka-TestKit TestProbe correctly?
我扩展了 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 的等待时间参数在测试多个 actor 时毫无用处。
欢迎任何评论。
测试探针同步工作。 expectMsg
方法正在阻塞。这很好,因为您想 运行 在 一个线程 中进行测试并控制它。这样就没有竞争条件。
等待消息时的超时是从读取该行的时间开始计算的,这意味着 tester2.expectMsg(500.milliseconds, "Hello")
中的 500 毫秒将在 tester1
收到他的消息后才开始倒计时。
如果tester1
没有收到消息,你想立即通过测试,不需要继续tester2
。
另一方面,调度程序是在多线程中异步完成的,倒计时立即开始,因为没有阻塞。所以预定的400毫秒和800毫秒会(几乎)同时开始倒计时
因此,将 400 毫秒更改为 100 毫秒意味着第二条消息仍将在距离开始大约 800 毫秒后发送,但 tester1
将期望它从开始后 100 + 500 = 600 毫秒发送。这就是它失败的原因。
您可以像这样尝试使用 within
块:
within(800.milliseconds, 900.milliseconds){
tester1.expectMsg("Hello")
tester2.expectMsg("Hello")
}
这样,如果您注释掉 tester1
的断言,测试仍然会通过。
我扩展了 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 的等待时间参数在测试多个 actor 时毫无用处。
欢迎任何评论。
测试探针同步工作。 expectMsg
方法正在阻塞。这很好,因为您想 运行 在 一个线程 中进行测试并控制它。这样就没有竞争条件。
等待消息时的超时是从读取该行的时间开始计算的,这意味着 tester2.expectMsg(500.milliseconds, "Hello")
中的 500 毫秒将在 tester1
收到他的消息后才开始倒计时。
如果tester1
没有收到消息,你想立即通过测试,不需要继续tester2
。
另一方面,调度程序是在多线程中异步完成的,倒计时立即开始,因为没有阻塞。所以预定的400毫秒和800毫秒会(几乎)同时开始倒计时
因此,将 400 毫秒更改为 100 毫秒意味着第二条消息仍将在距离开始大约 800 毫秒后发送,但 tester1
将期望它从开始后 100 + 500 = 600 毫秒发送。这就是它失败的原因。
您可以像这样尝试使用 within
块:
within(800.milliseconds, 900.milliseconds){
tester1.expectMsg("Hello")
tester2.expectMsg("Hello")
}
这样,如果您注释掉 tester1
的断言,测试仍然会通过。