我有一个 Akka 演员的 Scala 单元测试。Actor 旨在轮询远程系统并更新本地缓存。Actor 的部分设计是它不会在仍在处理或等待最后一次轮询的结果时尝试轮询,以避免在远程系统遇到减速时淹没远程系统。
我有一个测试用例(如下所示),它使用 Mockito 来模拟慢速网络调用,并检查当参与者被告知更新时,它不会在当前调用完成之前进行另一个网络调用。它通过验证缺少与远程服务的交互来检查参与者没有进行另一次调用。
我想消除对Thread.sleep
. 我想在每次测试运行中测试演员的功能而不依赖于等待硬编码的时间,这很脆弱,而且浪费时间。测试可以轮询或阻塞,等待条件,超时。这将更加健壮,并且不会在测试通过时浪费时间。我还有一个额外的约束,我希望将用于防止额外轮询的状态var allowPoll
限制在PollingActor
.
- 有没有办法强制等待演员自己完成消息传递?如果有办法我可以等到那时再尝试断言。
- 是否有必要发送内部消息?我不能使用线程安全的数据结构来维护内部状态,例如
java.util.concurrent.AtomicBoolean
. 我已经这样做了,代码似乎可以工作,但我对 Akka 的了解不足,无法知道它是否不受欢迎——一位同事推荐了 self 消息样式。 - 是否有更好的、开箱即用的具有相同语义的功能?然后我会选择集成测试而不是单元测试,尽管我不确定它是否能解决这个问题。
当前的演员看起来像这样:
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
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
}