如何让我的本地演员更可测试?

时间:2016-11-29 11:52:22

标签: scala unit-testing refactoring akka

我有一个演员系统大致如下:

class LocalActor extends Actor {

  // create the remote actor
  val remoteActor = context.actorSelection("akka.tcp://RemoteSystem@127.0.0.1:2552/user/RemoteActor")

  def receive = {
    case foo: String => remoteActor ! s"foo = ${foo}"
    case bar: Int => remoteActor ! s"bar = ${bar}"
    case _ => remoteActor ! "No clue..."
  }
}

我想重构它,因此remoteActor的TCP / IP不是硬编码的。最简单的更改是将其显式传递给构造函数:

class LocalActor(TcpIp: String) extends Actor {

    val remoteActor = context.actorSelection(TcpIp)
    // ...
}

但我担心这可能会导致TCP / IP地址已被使用的问题。对我来说最有意义的选项是将actor(或引用或类似的)传递给构造函数 - 在Akka中有一种惯用的方法吗?

我有点困惑,因为remoteActor的类型是akka.actor.ActorSelection,我可能认为它是ActorActorRef

幸运的是,远程角色只与sender进行交互,所以它的方式很好;但当地演员仍然很棘手。

如果我上面的想法不是一个好的,那么传统的方法是什么使它更通用和可测试?

1 个答案:

答案 0 :(得分:3)

实现此目的的一种方法是直接将引用传递给您的actor类:

object LocalActor {
  def prop(remoteActor: ActorRef) = Props(new LocalActor(remoteActor))
}

class LocalActor(remoteActor: ActorRef) extends Actor {
  def receive = {
    case foo: String => remoteActor ! s"foo = ${foo}"
    case bar: Int => remoteActor ! s"bar = ${bar}"
    case _ => remoteActor ! "No clue..."
  }
}

然后,无论您在何处创建LocalActor,还可以通过解析remoteActor来创建对actorSelection的引用:

val system = ActorSystem("yourSystem")
implicit val resolveTimeout = Timeout(5 seconds)
val remoteActor = Await.result(system.actorSelection("akka.tcp://RemoteSystem@127.0.0.1:2552/user/RemoteActor").resolveOne(), resolveTimeout.duration)
val localActor = system.actorOf(LocalActor.props(remoteActor), "LocalActor")

然后,为了进行测试,您只需要注入TestProbe

val testProbe = TestProbe()
val testingLocalActor = system.actorOf(LocalActor.props(testProbe.ref))
val testString = "TEST"
testingLocalActor ! testString
testProbe.expectMsg(s"foo = $testString")