Eug*_*Loy 5 testing scala akka akka-testkit akka-monitoring
在我的场景中,我有2个演员:
watchee(我用TestProbe)watcher(Watcher包裹在我的测试中TestActorRef暴露一些内部stateI轨道)守望者应该在watchee死亡时采取一些行动.
这是我到目前为止编写的完整测试用例:
class TempTest(_system: ActorSystem) extends TestKit(_system) with ImplicitSender with FunSuiteLike with Matchers with BeforeAndAfterAll {
def this() = this(ActorSystem("TempTest"))
override def afterAll {
TestKit.shutdownActorSystem(system)
}
class WatcherActor(watchee: ActorRef) extends Actor {
var state = "initial"
context.watch(watchee)
override def receive: Receive = {
case "start" =>
state = "start"
case _: Terminated =>
state = "terminated"
}
}
test("example") {
val watchee = TestProbe()
val watcher = TestActorRef[WatcherActor](Props(new WatcherActor(watchee.ref)))
assert(watcher.underlyingActor.state === "initial")
watcher ! "start" // "start" will be sent and handled by watcher synchronously
assert(watcher.underlyingActor.state === "start")
system.stop(watchee.ref) // will cause Terminated to be sent and handled asynchronously by watcher
Thread.sleep(100) // what is the best way to avoid blocking here?
assert(watcher.underlyingActor.state === "terminated")
}
}
Run Code Online (Sandbox Code Playgroud)
现在,因为所有涉及的actor都使用CallingThreadDispatcher(所有Akka的测试助手都使用props构造.withDispatcher(CallingThreadDispatcher.Id))我可以安全地假设当这个语句返回时:
watcher ! "start"
Run Code Online (Sandbox Code Playgroud)
..."开始"消息已经被处理WatchingActor,因此我可以根据该消息进行断言watcher.underlyingActor.state
但是,根据我的观察,当我停止watchee使用system.stop或发送Kill给它时,Terminated作为watchee死亡的副作用产生的消息在另一个线程中异步执行.
不,一个解决方案是停止watchee,块线程一段时间,验证Watcher之后的状态,但我想知道我该怎么做正确的方式(即如何确保杀死男主角后,它的观察者接收和处理 Terminated消息表明它死了)?
解决此问题的一种方法是在您的测试中引入另一个观察者,同时观看watchee.这个其他观察者是一个TestProbe允许我们对它执行断言的东西,它将摆脱你所看到的时间问题.一,修改后的测试代码:
val watchee = TestProbe()
val watcher = TestActorRef[WatcherActor](Props(new WatcherActor(watchee.ref)))
val probeWatcher = TestProbe()
probeWatcher watch watchee.ref
assert(watcher.underlyingActor.state === "initial")
watcher ! "start" // "start" will be sent and handled by watcher synchronously
assert(watcher.underlyingActor.state === "start")
system.stop(watchee.ref) // will cause Terminated to be sent and handled asynchronously by watcher
probeWatcher.expectTerminated(watchee.ref)
assert(watcher.underlyingActor.state === "terminated")
Run Code Online (Sandbox Code Playgroud)
所以你可以看到我已经引入了额外的观察者:
val probeWatcher = TestProbe()
probeWatcher watch watchee.ref
Run Code Online (Sandbox Code Playgroud)
然后,在代码的后面,在你失败的最终断言之前,我使用另一个断言让我知道Terminated已停止的actor 的消息已经正确分发:
probeWatcher.expectTerminated(watchee.ref)
当代码移过这一行时,我可以确定watcher欠测试也收到了它终止的消息,并且跟随的断言将通过.
编辑
如OP所述,此代码存在一定程度的非确定性.另一种可能的解决方案是更改测试代码中停止actor的行:
watcher.underlyingActor.context.stop(watchee.ref)
Run Code Online (Sandbox Code Playgroud)
通过使用context的TestActorRef相信Terminated都将通过交付CallingThreadDispatcher,因此是完全同步的.我在一个循环中测试了这个,它对我来说超过1000次迭代.
现在我想,也许是因为我正在stop使用相同的演员,期待Terminated可能有一个优化来Terminated为该scanario 提供自我,所以我也测试了一个完全不同Actor的如下:
class FooActor extends Actor{
def receive = {
case _ =>
}
Run Code Online (Sandbox Code Playgroud)
然后在测试代码中:
val foo = TestActorRef(new FooActor)
Run Code Online (Sandbox Code Playgroud)
并在停止:
foo.underlyingActor.context.stop(watchee.ref)
Run Code Online (Sandbox Code Playgroud)
这也按预期工作.
编辑:经过与 OP 的讨论和测试,我们发现发送 PoisonPill 作为终止所监视的 Actor 的方法可以实现所需的行为,因为 PPill 的终止是同步处理的,而 stop 或终止的终止是异步处理的。
虽然我们不确定原因,但我们最好的猜测是,这是因为杀死演员会引发异常,而 PPilling 则不会。
显然,这与按照我最初的建议使用 GracefulStop 模式无关,我将在下面报告该建议。
总之,OP问题的解决方案只是终止被监视的actor发送PPill而不是Kill消息或通过执行system.stop。
旧答案从这里开始:
我可能会提出一些不相关的建议,但我觉得它可能适用。
如果我理解正确的话,你想要做的基本上是同步终止演员,即只在演员正式死亡并且其死亡已被记录(在你的情况下由观察者记录)后才返回。
一般来说,死亡通知以及 akka 中的大多数其他通知都是异步的。尽管如此,使用 GracefulStop 模式 (akka.pattern.gracefulStop) 可以获得同步死亡确认。
为此,代码应类似于:
val timeout = 5.seconds
val killResultFuture = gracefulStop(victimRef, timeout, PoisonPill)
Await.result(killResultFuture, timeout)
Run Code Online (Sandbox Code Playgroud)
其作用是向受害者发送 PoisonPill(注意:您可以使用自定义消息),受害者将回复一个在受害者死亡后完成的 future。使用 Await.result 保证你是同步的。
不幸的是,这仅在以下情况下可用:a)您正在主动杀死受害者,并且您不想响应外部死亡原因 b)您可以在代码中使用超时和阻塞。但也许您可以根据您的情况调整此模式。
| 归档时间: |
|
| 查看次数: |
2555 次 |
| 最近记录: |