首页 > 解决方案 > sender() 导致 deadLetters 用于实现但不在单元测试中

问题描述

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

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

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

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

一些示例代码可以让您了解 unittest 结构

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

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

标签: scalaakka

解决方案


调用sender作为参与者的一部分执行的代码Future是不确定的(即竞争条件),因为您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
  }
}

推荐阅读