unit-testing - 如何在不使用 Thread.sleep 的情况下对向自身发送消息的 Akka Actor 进行单元测试

标签 unit-testing scala concurrency akka

我有一个 Akka Actor 的 Scala 单元测试。 Actor 旨在轮询远程系统并更新本地缓存。 Actor 的部分设计是它不会在仍在处理或等待最后一次轮询的结果时尝试轮询,以避免在远程系统遇到减速时淹没远程系统。

我有一个测试用例(如下所示),它使用 Mockito 来模拟慢速网络调用,并检查当参与者被告知更新时,它不会在当前调用完成之前进行另一个网络调用。它通过验证缺少与远程服务的交互来检查参与者没有进行另一次调用。

我想取消对 Thread.sleep 的调用.我想在每次测试运行中测试 Actor 的功能而不依赖于等待硬编码的时间,这很脆弱,而且浪费时间。测试可以轮询或阻塞,等待条件,超时。这将更加健壮,并且不会在测试通过时浪费时间。我还有一个额外的约束,我想保留用于防止额外轮询的状态 var allowPoll范围有限,仅限于 PollingActor 的内部.

  • 有没有办法强制等待 Actor 自己完成消息传递?如果有办法我可以等到那时再尝试断言。
  • 是否有必要发送内部消息?我不能用线程安全的数据结构来维护内部状态,例如 java.util.concurrent.AtomicBoolean .我已经这样做了,代码似乎可以工作,但我对 Akka 的了解不足,无法知道它是否不受欢迎——一位同事推荐了 self 消息样式。
  • 是否有更好的、开箱即用的具有相同语义的功能?然后我会选择集成测试而不是单元测试,尽管我不确定它是否能解决这个问题。


  • 当前的 Actor 看起来像这样:
    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
    }
    

    最佳答案

    我们现在选择解决这个问题的方法是将等效的观察者注入(inject)到参与者中(搭载未包含在问题列表中的现有记录器)。然后,actor 可以告诉观察者它何时从各种状态转换。在测试代​​码中,我们执行一个 Action ,然后等待参与者的相关通知,然后继续并做出断言。

    在测试中,我们有这样的东西:

    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
    

    我不知道是否有更惯用的方式,这对我来说似乎是一个合理的建议。

    关于unit-testing - 如何在不使用 Thread.sleep 的情况下对向自身发送消息的 Akka Actor 进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18364286/

    相关文章:

    asp.net-mvc - 测试使用 User.Identity.Name 的 Controller 操作

    scala - 如何从递归生成值的流创建 akka-stream 源?

    scala - 如何在 scala 中获取(不可变和可变)集合的列表?

    android - Scala Android AsyncTask 不会调用 publishProgress 回调, "onProgressUpdate"

    c++ - 具有宽松内存顺序的 fetch_add 会返回唯一值吗?

    python - 一个模拟/ stub python 模块如何像 urllib

    java - 时间和第二期显示 1 :10 instead of 13:10

    c# - 使用伪随机数生成器 (PRNG) 测试多个输入

    multithreading - 并发插入和更新 postgres

    php - 处理并发问题的最佳方法