Akka アクターの Scala 単体テストがあります。アクターは、リモート システムをポーリングし、ローカル キャッシュを更新するように設計されています。アクターの設計の一部は、スローダウンが発生したときにリモート システムがフラッディングするのを避けるために、最後のポーリングの処理中または結果を待っている間はポーリングを試行しないことです。
Mockito を使用して遅いネットワーク呼び出しをシミュレートし、アクターが更新するように指示されたときに、現在のネットワーク呼び出しが完了するまで別のネットワーク呼び出しを行わないことを確認するテスト ケース (以下に示す) があります。リモート サービスとの対話がないことを確認することで、アクターが別の呼び出しを行っていないことを確認します。
への呼び出しをなくしたいThread.sleep
。ハードコーディングされた時間を待機することなく、すべてのテスト実行でアクターの機能をテストしたいのですが、これは脆く、時間を無駄にします。テストは、タイムアウトを使用して、条件を待ってポーリングまたはブロックできます。これにより、より堅牢になり、テストに合格したときに時間を無駄にすることはありません。var allowPoll
また、スコープ内で制限された余分なポーリングを防ぐために使用される状態を の内部に保持するという追加の制約もありPollingActor
ます。
- アクターが自分自身にメッセージを送信し終わるまで強制的に待機させる方法はありますか? 主張する前にそれまで待つことができる方法があれば。
- 内部メッセージを送信する必要はありますか? などのスレッドセーフなデータ構造で内部状態を維持できませんでしたか
java.util.concurrent.AtomicBoolean
。私はこれを行っており、コードは機能しているように見えますが、Akka について十分な知識がなく、それが推奨されていないかどうかを知ることができません。同僚がセルフ メッセージ スタイルを推奨しました。 - 同じセマンティクスを備えた、すぐに使える優れた機能はありますか? 次に、単体テストではなく統合テストを選択しますが、それでこの問題が解決するかどうかはわかりません。
現在のアクターは次のようになります。
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
}