在阅读了更多关于 akka 的内容后,我终于找到了一个更好的解决方案:将 actor 邮箱替换为我在测试中可以观察到的邮箱。这样我就可以等到演员在我完成承诺后收到一条新消息。只有这样才能发送下一条消息。这篇TestingMailbox
文章的最后给出了代码。
更新:在 Akka Typed 中,这可以通过BehaviorInterceptor
. 只需使用自定义拦截器包装Behavior
被测对象,该拦截器转发所有消息和信号,但让您观察它们。下面给出了无类型 Akka 的邮箱解决方案。
演员可以这样配置:
actorUnderTest = system.actorOf(Props[MyActor]).withMailbox("testing-mailbox"))
我必须通过提供配置来确保参与者系统知道“测试邮箱”:
class MyTest extends TestKit(ActorSystem("some name",
ConfigFactory.parseString("""{
testing-mailbox = {
mailbox-type = "my.package.TestingMailbox"
}
}""")))
with BeforeAndAfterAll // ... and so on
设置好之后,我可以像这样更改我的测试:
myActor ! Message("A")
val nextMessage = TestingMailbox.nextMessage(actorUnderTest)
promiseFromApiCall.success(Message("B"))
Await.ready(nextMessage, 3.seconds)
myActor ! Message("C")
使用一点辅助方法,我什至可以这样写:
myActor ! Message("A")
receiveMessageAfter { promiseFromApiCall.success(Message("B")) }
myActor ! Message("C")
这是我的自定义邮箱:
import akka.actor.{ActorRef, ActorSystem}
import akka.dispatch._
import com.typesafe.config.Config
import scala.concurrent.{Future, Promise}
object TestingMailbox {
val promisesByReceiver =
scala.collection.concurrent.TrieMap[ActorRef, Promise[Any]]()
class MessageQueue extends UnboundedMailbox.MessageQueue {
override def enqueue(receiver: ActorRef, handle: Envelope): Unit = {
super.enqueue(receiver, handle)
promisesByReceiver.remove(receiver).foreach(_.success(handle.message))
}
}
def nextMessage(receiver: ActorRef): Future[Any] =
promisesByReceiver.getOrElseUpdate(receiver, Promise[Any]).future
}
class TestingMailbox extends MailboxType
with ProducesMessageQueue[TestingMailbox.MessageQueue] {
import TestingMailbox._
def this(settings: ActorSystem.Settings, config: Config) = this()
final override def create(owner: Option[ActorRef],
system: Option[ActorSystem]) =
new MessageQueue()
}