Akka Testkit AutoPilot
documentation的示例表明,调用TestProbe
之后,我们可以立即向setAutoPilot
发送消息:
probe.setAutoPilot(new TestActor.AutoPilot {
def run(sender: ActorRef, msg: Any): TestActor.AutoPilot =
msg match {
case "stop" ⇒ TestActor.NoAutoPilot
case x ⇒ testActor.tell(x, sender); TestActor.KeepRunning
}
})
//#autopilot
probe.ref ! "hallo"
另一方面,setAutoPilot
已实现为向testActor
发送消息:
def setAutoPilot(pilot: TestActor.AutoPilot): Unit = testActor ! TestActor.SetAutoPilot(pilot)
根据Akka message receive order guarantees,testActor
(probe.ref
)无法在"hallo"
之前接收TestActor.SetAutoPilot(pilot)
,因为两者都是从同一来源发送的
但是,如果我们使用第三个演员(使用system.actorOf(...)
创建)将"hello"
发送到probe.ref
,
在某些情况下,是否有可能在probe.ref
之前被TestActor.SetAutoPilot(pilot)
收到,从而最终被忽略了?
答案 0 :(得分:0)
从理论上讲-是的,绝对是-这基本上是您对问题的回答。在实践中,这不太可能,因为其他消息沿更长的路径传播,因此需要发生一些非常不寻常的事情,以便其更早到达。
由于从理论上讲不会给出任何可行的答案,因此我编写了一个检验以进行实证观察:
Build.sbt:
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.5.17"
)
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-testkit" % "2.5.17",
"org.scalactic" %% "scalactic" % "3.0.5",
"org.scalatest" %% "scalatest" % "3.0.5",
"org.scalacheck" %% "scalacheck" % "1.14.0"
) map (_ % "test")
测试:
import scala.concurrent.duration.DurationInt
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.ask
import akka.testkit.{TestActor, TestKit, TestProbe}
import akka.util.Timeout
import org.scalatest.{Matchers, PropSpecLike}
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.prop.PropertyChecks
class AutopilotTest extends TestKit(ActorSystem("test"))
with PropSpecLike with PropertyChecks with ScalaFutures with Matchers {
private implicit val askTimeout: Timeout = Timeout(100.millis)
property("Test probe receives autopilot before any other message from same origin") {
forAll(minSuccessful(1000)) { msg: String =>
val probe = TestProbe()
probe.setAutoPilot((sender: ActorRef, msg: Any) => msg match {
case x => sender ! x; TestActor.KeepRunning
})
whenReady((probe.ref ? msg).mapTo[String]) {_ shouldBe msg}
}
}
private class ProxyActor(target: ActorRef) extends Actor {
override def receive: Receive = { case msg: Any => target forward msg }
}
private object ProxyActor { def props(target: ActorRef): Props = Props(new ProxyActor(target)) }
property("Test probe receives autopilot before any other message from other origin") {
// set minSuccessuful to as high as you want, but note that current version takes ~38 seconds on my laptop to run
forAll(minSuccessful(1000)) { msg: String =>
val probe = TestProbe()
val proxy = system.actorOf(ProxyActor.props(probe.ref))
val result = (proxy ? msg).mapTo[String]
probe.setAutoPilot((sender: ActorRef, msg: Any) => msg match {
case x => sender ! x; TestActor.KeepRunning
})
whenReady(result) {_ shouldBe msg}
}
}
}
实际上,我对所有第二个测试都进行了10000次重复测试,并且测试始终通过-我还确保了如果在向代理发送消息后设置了自动驾驶仪,或者如果testProbe没有响应,则它会失败
因此,我想说的是,您所描述的潜在问题根本没有发生,或者极不可能发生。当然,该测试是一种非常简单的测试,因此在其他条件下(例如,阻塞,并行测试执行,CI等),观察结果可能有所不同,但至少它为最常见/最简单的情况提供了一些证据。