玩! scala和Akka:如何测试演员A是否向演员B发送了一条消息?

时间:2016-11-07 09:57:13

标签: scala testing playframework akka actor

我想测试演员A在收到消息后向演员B发送消息。

我正在玩Play! 2.5我使用工厂,因为我需要在演员内部注入一些类和wSClient之类的东西。

演员A看起来像:

object ActorA {
  trait Factory {
    def apply(ec: ExecutionContext, actorBRef: ActorRef): Actor
  }
}

class ActorA @Inject()(implicit val ec: ExecutionContext,
                       @Named("actor-b") actorBRef: ActorRef)
    extends Actor with ActorLogging with InjectedActorSupport {

  override def receive: Receive = {
    case i: Long =>
      log info s"received $i"
      actorBRef ! (i+1)
}

演员B更简单:

object ActorB {
  trait Factory {
    def apply(): Actor
  }
}

class ActorB extends Actor with ActorLogging {

  override def receive: Receive = {
    case _ =>
      log error "B received an unhandled message"
  }
}

但是我的测试没有通过,据说预期的消息没有到达,我在测试中得到了一个超时(但它被演员B记录得很好)所以问题来自于测试(和可能是探测器。)

以下是测试:

  val actorBProbe = TestProbe()
  lazy val appBuilder = new GuiceApplicationBuilder().in(Mode.Test)
  lazy val injector = appBuilder.injector()
  lazy val factory = injector.instanceOf[ActorA.Factory]
  lazy val ec = scala.concurrent.ExecutionContext.Implicits.global
  lazy val factoryProps = Props(factory(ec, actorBProbe.ref))
  val ActorARef = TestActorRef[ActorA](factoryProps)

  "Actor B" must {

    "received a message from actor A" in {
      ActorARef ! 5L

      actorBProbe.expectMsg(6L)
    }
  }

我还创建了一个最小游戏!应用上面的代码here

1 个答案:

答案 0 :(得分:1)

在你的测试中,actorBProbe不是传递给ActorA构造函数(ref ActorARef)的ActorB ref。真正发生的是Guice创建了一个不同的ActorB(名为actor-b),并将其ref传递给ActorA(ref ActorARef)构造函数。

测试结束时,ActorB actor-b接收6L(如日志中所示)。虽然actorBProbe没有收到任何结果。

混淆真的来自Guice lifecyle和Actors。根据我的经验,它会产生比我能承受的更多的痛苦。

要证明,只需打印ActorRef的哈希码,您就会发现它们不同。说明如下:

val actorBProbe = TestProbe()
println("actorBProbe with ref hash: " + actorBProbe.ref.hashCode())

class ActorA ... {
  override def preStart =
    log error "preStart actorBRef: " + actorBRef.hashCode()

  // ...
}

事实上,在ActorA中,即使ec与测试代码中的ec不同。

以下是一种强迫"通过测试,同时证明{}未真正被actorBProbe使用。

而不是依靠Guice来"连接"演员B,我们告诉Guice将@Named("actor-b")替换为@Assisted,就像这样,

import ...
import com.google.inject.assistedinject.Assisted

class ActorA @Inject()(...
  /*@Named("actor-b")*/ @Assisted actorBRef: ActorRef)
...

重新进行测试,它会通过。但这可能不是你想要的开始。