首页 > 解决方案 > 如何在集群分片测试环境中模拟演员?

问题描述

我已经设法使用 akka 和 scala 为事件源行为设置了一个测试环境,并且可以通过以下方式正确地对自包含的 Actor 运行单元测试

class CQRSActorSpec
    extends ScalaTestWithActorTestKit(
      EventSourcedBehaviorTestKit.config.withFallback(CQRSActorSpec.config)
    )

然后创建我的测试包

private val myTestKit = EventSourcedBehaviorTestKit[Command, Event, State](system, MyActor)

并用它来发出命令

val result = myTestKit.runCommand[Response](StartJob(parameters, _))
result.reply shouldBe Done
result.event shouldBe Started(parameters)
result.state shouldBe ProcessingJob

现在我想对一个在其生命周期中调用另一个actor的actor进行单元测试,这是因为我正在使用saga模式,所以我正在测试的actor是saga的主管,并且必须调用相关方。

到目前为止,我设法做到了以下几点:

val mockParty = Behaviors.receiveMessage[Party.Command] { msg =>
  val reply = msg match {
    case _ => Done
  }
  msg.replyTo ! reply
  Behaviors.same
}

ClusterSharding(system).init(Entity(Party.Key) { _ => mockParty })

这在第一次测试中运行正常,但是当我必须测试另一个时,比如说失败案例,第二次调用当然不会工作,因为集群分片中已经注册了一个实体,我无法覆盖该行为。此外,没有办法重置集群分片。

有没有人知道如何解决这个问题?还有其他我不知道的用于测试集群分片的实用程序吗?我发现文档有点缺乏。

标签: scalaakka-clusterakka-persistenceakka-testkitakka-typed

解决方案


问题的根源是ClusterSharding(system)在您的代码中使用它将创建真实ClusterSharding对象,就像您说您无法控制测试一样。相反,您应该ClusterSharding从外部传递对象,以便您可以传递它的存根/模拟实现。

这是我所说的用 Java 渲染的大纲:

让我们假设 type 的持久性 actor/event 来源行为EntityA将创建 type 的另一个持久性 actor/event 来源行为并与之交互EntityB

在您的应用程序代码中的某个地方,您将ClusterSharding.init(..)调用EntityA

ClusterSharding sharding = ClusterSharding.get(actorSystem); //the real stuff in the app
sharding.init(
      Entity.of(
        EntityA.ENTITY_TYPE_KEY,
        entityContext ->
          EntityA.create(
            entityContext.getEntityId(),
            sharding
          )
      )
    );

注意sharding对象是如何传递给create方法的,EntityA并且是与之交互的ClusterSharding对象。EntityA

在单元测试中,不会调用此代码,相反,您可以ClusterSharding在初始化 testKit 时注入自己的实现:

//this is in test
ClusterSharding sharding = new ClusterShardingStub(); //your stub/mock
    var esbtk =
      EventSourcedBehaviorTestKit.create(
        testKit.system(),
        EntityA.create("entityId", sharding));

所以现在在你的EntityA实现逻辑的某个地方,你大概会调用sharding.entityRefFor(...)来获取EntityRef<EntityBProtocol>. 您需要做的是对您的存根/模拟进行编程以返回TestEntityRef.of(..)实例:

TestProbe<EntityBProtocol> testProbe = TestProbe.create(testKit.system());


EntityRef<EntityBProtocol> ref = TestEntityRef.of(EntityB.ENTITY_TYPE_KEY, "entityId", testProbe.ref());

现在,任何EntityA与 with的交互EntityB都可以使用该TestProbe实例进行断言。


推荐阅读