6

Akka アクターの Scala 単体テストがあります。アクターは、リモート システムをポーリングし、ローカル キャッシュを更新するように設計されています。アクターの設計の一部は、スローダウンが発生したときにリモート システムがフラッディングするのを避けるために、最後のポーリングの処理中または結果を待っている間はポーリングを試行しないことです。

Mockito を使用して遅いネットワーク呼び出しをシミュレートし、アクターが更新するように指示されたときに、現在のネットワーク呼び出しが完了するまで別のネットワーク呼び出しを行わないことを確認するテスト ケース (以下に示す) があります。リモート サービスとの対話がないことを確認することで、アクターが別の呼び出しを行っていないことを確認します。

への呼び出しをなくしたいThread.sleep。ハードコーディングされた時間を待機することなく、すべてのテスト実行でアクターの機能をテストしたいのですが、これは脆く、時間を無駄にします。テストは、タイムアウトを使用して、条件を待ってポーリングまたはブロックできます。これにより、より堅牢になり、テストに合格したときに時間を無駄にすることはありません。var allowPollまた、スコープ内で制限された余分なポーリングを防ぐために使用される状態を の内部に保持するという追加の制約もありPollingActorます。

  1. アクターが自分自身にメッセージを送信し終わるまで強制的に待機させる方法はありますか? 主張する前にそれまで待つことができる方法があれば。
  2. 内部メッセージを送信する必要はありますか? などのスレッドセーフなデータ構造で内部状態を維持できませんでしたかjava.util.concurrent.AtomicBoolean。私はこれを行っており、コードは機能しているように見えますが、Akka について十分な知識がなく、それが推奨されていないかどうかを知ることができません。同僚がセルフ メッセージ スタイルを推奨しました。
  3. 同じセマンティクスを備えた、すぐに使える優れた機能はありますか? 次に、単体テストではなく統合テストを選択しますが、それでこの問題が解決するかどうかはわかりません。

現在のアクターは次のようになります。

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
}
4

1 に答える 1

4

これを解決するために今のところ選択した方法は、オブザーバーに相当するものをアクターに注入することです (質問のリストに含まれていなかった既存のロガーに便乗します)。アクターは、さまざまな状態からいつ遷移したかをオブザーバーに伝えることができます。テスト コードでは、アクションを実行し、アクターからの関連する通知を待ってから、続行してアサーションを作成します。

テストでは、次のようなものがあります。

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

もっと慣用的な方法があるかどうかはわかりませんが、これは私にとって合理的な提案のようです。

于 2013-08-21T17:46:04.277 に答える