[英]How to unit test an Akka actor that sends a message to itself, without using Thread.sleep
I have a Scala unit test for an Akka actor. 我有一个Akka演员的Scala单元测试。 The actor is designed to poll a remote system and update a local cache.
该actor旨在轮询远程系统并更新本地缓存。 Part of the actor's design is that it doesn't attempt to poll while it's still processing or awaiting the result of the last poll, to avoid flooding the remote system when it experiences a slowdown.
演员设计的一部分是它在处理或等待上次轮询的结果时不会尝试轮询,以避免在经历速度减慢时充斥远程系统。
I have a test case (shown below) which uses Mockito to simulate a slow network call, and checks that when the actor is told to update, it won't make another network call until the current one is complete. 我有一个测试用例(如下所示),它使用Mockito来模拟一个慢速网络呼叫,并检查当告知演员更新时,它将不会进行另一个网络呼叫,直到当前完成。 It checks the actor has not made another call by verifying a lack of interactions with the remote service.
它通过验证缺少与远程服务的交互来检查actor没有进行另一次调用。
I want to eliminate the call to Thread.sleep
. 我想取消对
Thread.sleep
的调用。 I want to test the functionality of the actor without relying on waiting for a hardcoded time, in every test run, which is brittle, and wastes time. 我想测试演员的功能而不依赖于等待硬编码时间,在每次测试运行中都很脆弱,浪费时间。 The test can poll or block, waiting for a condition, with a timeout.
测试可以轮询或阻止,等待条件,超时。 This will be more robust, and will not waste time when the test is passing.
这将更加强大,并且在测试通过时不会浪费时间。 I also have the added constraint that I want to keep the state used to prevent extra polling
var allowPoll
limited in scope, to the internals of the PollingActor
. 我还有一个额外的约束,我想保持状态用于防止额外轮询
var allowPoll
在范围内限制到PollingActor
的内部。
java.util.concurrent.AtomicBoolean
. java.util.concurrent.AtomicBoolean
。 I have done this and the code appears to work, but I'm not knowledgeable enough about Akka to know if it's discouraged -- a colleague recommended the self message style. The current actor looks something like this: 当前的演员看起来像这样:
class PollingActor(val remoteService: RemoteServiceThingy) extends ActWhenActiveActor {
private var allowPoll: Boolean = true
def receive = {
case PreventFurtherPolling => {
allowPoll = false
}
case AllowFurtherPolling => {
allowPoll = true
}
case UpdateLocalCache => {
if (allowPoll) {
self ! PreventFurtherPolling
remoteService.makeNetworkCall.onComplete {
result => {
self ! AllowFurtherPolling
// process result
}
}
}
}
}
}
trait RemoteServiceThingy {
def makeNetworkCall: Future[String]
}
private case object PreventFurtherPolling
private case object AllowFurtherPolling
case object UpdateLocalCache
And the unit test, in specs2, looks like this: 在specs2中的单元测试看起来像这样:
"when request has finished a new requests can be made" ! {
val remoteService = mock[RemoteServiceThingy]
val actor = TestActorRef(new PollingActor(remoteService))
val slowRequest = new DefaultPromise[String]()
remoteService.makeNetworkCall returns slowRequest
actor.receive(UpdateLocalCache)
actor.receive(UpdateLocalCache)
slowRequest.complete(Left(new Exception))
// Although the test calls the actor synchronously, the actor calls *itself* asynchronously, so we must wait.
Thread.sleep(1000)
actor.receive(UpdateLocalCache)
there was two(remoteService).makeNetworkCall
}
The way we have chosen to solve this for now is to inject the equivalent of an observer into the actor (piggybacking on an existing logger which wasn't included in the listing in the question). 我们现在选择解决这个问题的方法是将一个观察者的等价物注入演员(捎带现有的记录器,这个记录器没有包含在问题列表中)。 The actor can then tell the observer when it has transitioned from various states.
然后,演员可以告诉观察者它何时从各种状态转变。 In the test code we perform an action then wait for the relevant notification from the actor, before continuing and making assertions.
在测试代码中,我们执行一个操作,然后等待来自actor的相关通知,然后继续并进行断言。
In the test we have something like this: 在测试中,我们有这样的事情:
actor.receive(UpdateLocalCache)
observer.doActionThenWaitForEvent(
{ actor.receive(UpdateLocalCache) }, // run this action
"IgnoredUpdateLocalCache" // then wait for the actor to emit an event
}
// assert on number of calls to remote service
I don't know if there's a more idiomatic way, this seems like a reasonable suggestion to me. 我不知道是否有更惯用的方式,这对我来说似乎是一个合理的建议。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.