Akka testkit和underlyingActor的内部状态

时间:2014-01-06 18:34:58

标签: scala

我试图在我的单元测试中获得演员的内部状态,但由于某种原因旧状态仍然存在。

我的演员应该添加/删除/列出自我注册的演员服务:

class DirectoryServiceActor extends Actor {
  var servicesMap: Map[String, List[ActorRef]] = Map.empty[String, List[ActorRef]]

  def receive = {
    case AddService(serviceType) ⇒
      servicesMap = servicesMap + (serviceType -> (sender :: servicesMap.getOrElse(serviceType, List.empty[ActorRef])))
      sender ! Ack
    case RemoveService ⇒
      val oldMap = servicesMap
      servicesMap = servicesMap.mapValues(list ⇒ (if (list.contains(sender)) list.diff(List(sender)) else list).toList)
      println(servicesMap)
      if (servicesMap.equals(oldMap)) {
        sender ! Nack
      } else {
        sender ! Ack
      }

    case ListServices ⇒
      sender ! services
  }

  def services: Map[String, List[ActorRef]] = this.servicesMap
}

我的测试是

"Remove existing service successfully" in {
  implicit val timeout = 10 millis

  val probe = new TestProbe(system)

  val directoryService = TestActorRef[DirectoryServiceActor]
  val actor = directoryService.underlyingActor

  directoryService.tell(AddService("test"), probe.ref)

  probe.expectMsg(timeout, Ack)

  directoryService.tell(RemoveService, probe.ref)

  probe.expectMsg(timeout, Ack)

  println("TEST: " + actor.services)

  actor.services("test") should not contain (probe.ref)
}

根据测试和控制台输出失败判断,似乎actor.underlyingActor.services返回旧值:

Map(test -> List())
TEST: Map(test -> List(Actor[akka://myApp/system/testActor3#-2080677614]))

即使在actor内部,该变量也已设置为新值。我错过了什么?

更新:实际上似乎与Akka无关,但可以在测试中使用期货来解决:

"Remove existing service successfully" in {
  implicit val timeout = Timeout(100 millis)

  val directoryService = TestActorRef[DirectoryServiceActor]

  val addResponseFuture = directoryService ? AddService(self, "test")

  addResponseFuture.value.get should be(Success(Ack(self)))

  val removeResponseFuture = directoryService ? RemoveService(self)

  removeResponseFuture.value.get should be(Success(Ack(self)))

  val listResponseFuture = directoryService ? ListServices

  listResponseFuture.value.get should be(Success(Map("test" -> List())))

  val actor = directoryService.underlyingActor
  actor.services("test") should not contain (self)

}

我认为由于mapValue实际上没有创建新地图而发生这种情况:Scala: Why mapValues produces a view and is there any stable alternatives?

1 个答案:

答案 0 :(得分:2)

出于某种原因,我认为mapValues正是导致问题的原因。尝试更改RemoveService处理,如下所示:

case RemoveService =>
  val oldMap = servicesMap      
  servicesMap = servicesMap.map{
    case (key, list) => (key, list.filterNot(_ == sender))
  }

  if (servicesMap.equals(oldMap)) {
    sender ! Nack
  } else {
    sender ! Ack
  }