简体   繁体   English

如何在不使用Thread.sleep的情况下对发送消息的Akka actor进行单元测试

[英]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的内部。

  1. is there a way force a wait until the actor is finished messaging itself? 有没有办法强制等待,直到演员完成消息传递? If there's a way I can wait until then before trying to assert. 如果有办法,我可以等到那时再尝试断言。
  2. is it necessary to send the internal message at all? 是否有必要发送内部消息? Couldn't I maintain the internal state with a threadsafe datastructure, such as 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. 我已经完成了这个并且代码似乎有效,但是我对Akka的了解不足以知道它是否沮丧 - 一位同事推荐了自我消息风格。
  3. is there better, out-of-the-box functionality with the same semantics? 有没有更好的,开箱即用的功能与相同的语义? Then I would opt for an integration test instead of a unit test, though I'm not sure if it would solve this problem. 然后我会选择集成测试而不是单元测试,虽然我不确定它是否能解决这个问题。

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.

相关问题 有没有办法在不使用 Thread.sleep 的情况下对 ScheduledExecutorService.scheduleAtFixedRate 进行单元测试? - Is there a way to unit test ScheduledExecutorService.scheduleAtFixedRate without using Thread.sleep? JUnit测试计划的影响逻辑,而无需使用Thread.sleep - JUnit Test scheduled impact logic without using Thread.sleep 如何在单元测试中避免 Thread.sleep? - How to avoid Thread.sleep in Unit tests? 如何对Akka.Net Actor状态进行单元测试(使用Begin()) - How to unit test Akka.Net Actor State (using Become()) 如何编写单元测试以测试Thread.Sleep的功能? - How can I write a unit test to test the functionality of Thread.Sleep? 单元测试在Thread.Sleep时间之前完成 - Unit Test Finishes before Thread.Sleep time 如何模拟一个Akka演员单元测试一个类? - How to mock an Akka Actor to Unit Test a class? Java power mock unit test 一种具有Thread.Sleep的方法 - Java power mock unit test a method having Thread.Sleep 如何在Akka actor中对Dispatch Http进行单元测试? - How to unit test Dispatch Http in an Akka actor? 如何对在执行程序服务中运行的代码片段进行单元测试,而不是等待 Thread.sleep(time) - How to unit test a code snippet running inside executor service, instead waiting on Thread.sleep(time)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM