sender() 导致执行死信,但不会导致单元测试

sender() results in deadLetters for implementation but not in unit tests

我正在使用 Akka 经典演员(运行在 Play 框架中)并且运行遇到 sender() 方法的问题。当我构建 运行 代码(使用 sbt run)时,我可以看到 sender() 解析为 deadLetters actor。但是,当 运行ning 对该 actor 进行单元测试时,sender() 解析为正确的 ActorRef(即使它不应该)。

sender() 正在不久的将来被调用,这就是为什么我在 运行 实际实施时看到 deadLetters。但是,出于某种原因,我在单元测试中没有看到 deadLetters 。关于为什么单元测试和 运行ning 实例之间存在行为差异的任何想法?

一些示例代码以了解代码结构

class MyActor extends Actor {
  private def throwError() = {
    // THIS IS WHERE sender() BEHAVIOR DIFFERENTIATES BETWEEN IMPLEMENTATION AND UNITTESTS
    sender() ! MyErrorMessage()

    throw new Exception()
  }

  private def myFutureMethod(args: SomeType): Future[Done] = {
    for {
       Some logic here...
    } yield {
      if (some logic check...)
        throwError()
      else
        Done
    }
  }

  override def stepReceive = {
    case MyMessage(args) => 
      myFutureMethod(args)
  }
}

一些示例代码来了解单元测试结构

  "My failure test case" in {
    val testProbe = TestProbe()

    myActorImpl.tell(MyMessage(args), testProbe)

    // testProbe SHOULD BE NOT BE RECEIVING A MESSAGE BASED ON THE ABOVE
    // IMPLEMENTATION, BUT IT DOES.
    testProbe.expectNoMessage()
  }

如果有人有一些调试技巧或想法,那就太好了。谢谢。

在演员中作为 Future 的一部分执行的代码中调用 sender 是不确定的(即竞争条件),因为您的 MyActor 可以继续处理另一个Future 中的代码执行之前的消息,如果从那时起,sender 将发生变化,可能会变为 deadLetters.

在您的测试中,如果 myActorImpl 尚未处理消息,sender 仍将指向测试探针。

假设您希望消息真正发送给发件人,您需要在将其传递给 Future 之前捕获它,按照这些行

class MyActor extends Actor {
  private def throwError(target: ActorRef) = {
    // uses the captured sender, no race condition
    target ! MyErrorMessage()

    throw new Exception()
  }

  private def myFutureMethod(args: SomeType, sender: ActorRef): Future[Done] = {
    for {
       Some logic here...
    } yield {
      if (some logic check...)
        throwError(sender)
      else
        Done
    }
  }

  override def stepReceive = {
    case MyMessage(args) => 
      myFutureMethod(args, sender)  // captures the sender reference
  }
}