Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 如何在不使用Thread.sleep的情况下对向自身发送消息的Akka参与者进行单元测试_Unit Testing_Scala_Concurrency_Akka - Fatal编程技术网

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

Unit testing 如何在不使用Thread.sleep的情况下对向自身发送消息的Akka参与者进行单元测试,unit-testing,scala,concurrency,akka,Unit Testing,Scala,Concurrency,Akka,我有一个Akka演员的Scala单元测试。actor用于轮询远程系统并更新本地缓存。actor设计的一部分是,它不会在仍在处理或等待最后一次轮询结果时尝试轮询,以避免在远程系统遇到减速时淹没远程系统 我有一个测试用例(如下所示),它使用Mockito来模拟一个缓慢的网络调用,并检查当参与者被告知更新时,在当前网络调用完成之前不会进行另一个网络调用。它通过验证是否缺少与远程服务的交互来检查参与者是否没有进行另一次调用 我想取消对Thread.sleep的调用。我想在每次测试运行中测试actor的功

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

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

我想取消对
Thread.sleep
的调用。我想在每次测试运行中测试actor的功能,而不依赖于等待硬编码的时间,这很脆弱,而且浪费时间。测试可以轮询或阻塞,等待条件并超时。这将更加健壮,并且在测试通过时不会浪费时间。我还添加了一个约束,我希望将用于防止额外轮询的状态
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
    
    "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
    }
    
    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
    
    规范2中的单元测试如下所示:

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

    目前我们选择的解决方法是将等效的观察者注入到参与者中(借助于问题清单中未包含的现有记录器)。然后,参与者可以告诉观察者何时从各种状态转换。在测试代码中,我们执行一个操作,然后等待参与者的相关通知,然后继续并做出断言

    在测试中,我们有如下内容:

    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
    
    "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
    }
    
    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
    
    我不知道是否有更惯用的方法,这对我来说似乎是一个合理的建议