Akka. 如何在Java中模拟子actor?

5
假设我有一个父级演员,他自己创建Actor。
public static class Parent extends UntypedActor {

private ActorRef child = context().actorOf(Props.create(Child.class));

    @Override
    public void onReceive(Object message) throws Exception {
        // do some stuff
        child.tell("some stuff", self());
    }
}

public static class Child extends UntypedActor {

    @Override
    public void onReceive(Object message) throws Exception {

    }
}

如何模拟这个子Actor?谷歌没有给我任何合理的结果。Akka的文档告诉我创建一个Actor是一种良好的实践。但是,如果连我的Actor都不能测试,我如何遵循这个实践呢?


2
问题的标题听起来有点夸张。 - Blessed Geek
3个回答

1

我使用类似于这个问题的答案中描述的探针:

如何为测试Akka系统模拟子Actor?

ActorSystem system = ActorSystem.create();

new JavaTestKit( system )
{{
  final JavaTestKit probe = new JavaTestKit( system );
  final Props props = Props.create( SupervisorActor.class );

  final TestActorRef<SupervisorActor> supervisorActor =
    TestActorRef.create( system, props, "Superman" );

  supervisorActor.tell( callCommand, getTestActor() );

  probe.expectMsgEquals(42);
  assertEquals(getRef(), probe.getLastSender());
}};

0

你可以采用几种方法来模拟 Child actor。 其中之一是将创建 Child actor 的代码从 Parent actor 中外部化。

为此,您需要重写 Parent actor 并将一个函数传递给它,该函数将创建 Child actor:

注意:由于 版本 2.5.0 中的废弃, 我们将使用 AbstractActor 替代 UntypedActor

public class Parent extends AbstractActor {

  private final ActorRef child;

  public Parent(Function<ActorRefFactory, ActorRef> childCreator) {
    this.child = childCreator.apply(getContext());
  }

  public static Props props(Function<ActorRefFactory, ActorRef> childCreator) {
    return Props.create(Parent.class, childCreator);
  }

  @Override
  public Receive createReceive() {
    return receiveBuilder()
        .matchEquals("send ping", s -> child.tell("ping", getSelf()))
        .match(String.class, System.out::println)
        .build();
  }
}

你的子物体将保持不变:

public class Child extends AbstractActor {

  @Override
  public Receive createReceive() {
    return receiveBuilder()
        .matchEquals("ping", s -> getSender().tell("pong", getSelf()))
        .build();
  }
}

现在在您的测试中,您可以使用探针演员引用来测试应该发送到Child演员的消息,即probe将充当Child演员模拟:

public class ParentTest {

  static ActorSystem system;

  @BeforeClass
  public static void setUpClass() {
    system = ActorSystem.create();
  }

  @AfterClass
  public static void tearDownClass() {
    TestKit.shutdownActorSystem(system);
    system = null;
  }

  @Test
  public void givenParent_whenSendPing_thenPingChild() {
    TestKit probe = new TestKit(system);

    Function<ActorRefFactory, ActorRef> childCreator = arf -> probe.getRef();

    ActorRef parentActor = system.actorOf(Parent.props(childCreator));

    probe.send(parentActor, "send ping");

    probe.expectMsgEquals("ping");
  }
}

因此,不要使用(在实际应用程序代码中将被使用):

Function<ActorRefFactory, ActorRef> childCreator = arf -> arf
                                      .actorOf(Props.create(Child.class));

我们将使用:

Function<ActorRefFactory, ActorRef> childCreator = arf -> probe.getRef();

检查probe是否收到了“ping”消息。

希望这有所帮助。有关给定方法的更多信息可以在此处找到。


0
以下代码将使用Scala编写,但我猜想同样适用于Java。答案是您可以使用TestProbe来模拟您的子Actor。让我们以这个例子为例:
import akka.actor.{Actor, Props}

class Parent extends Actor {
  import Parent._

  val child = context.actorOf(Child.props)

  override def receive: Receive = {
    case TellName(name) => child ! name
    case Reply(msg) => sender() ! msg
  }
}

object Parent {
  case class TellName(name: String)
  case class Reply(text: String)
  def props = Props(new Parent)
}

class Child extends Actor {
  override def receive: Actor.Receive = {
    case name: String => sender !  Parent.Reply(s"Hi there $name")
  }
}

object Child {
  def props = Props(new Child)
}

所以,我们有一个名为Parent的演员向Child演员发送消息TellName。在接收到消息Child演员将通过向其发送带有内容的Reply消息来回应其发送者 - 即“嗨,乔”。现在,这是一个测试:

class ParentSpec extends TestKit(ActorSystem("test")) with WordSpecLike with Matchers with ImplicitSender {


  val childProbe = TestProbe()

  "Parent actor" should {
    "send a message to child actor" in {
      childProbe.setAutoPilot(new AutoPilot {
        override def run(sender: ActorRef, msg: Any): AutoPilot = msg match {
          case name: String => sender ! Reply(s"Hey there $name")
            NoAutoPilot
        }
      })
      val parent = system.actorOf(Props(new Parent {
        override val child = childProbe.ref
      }))
      parent ! TellName("Johnny")
      childProbe.expectMsg("Johnny") // Message received by a test probe
      expectMsg("Hey there Johnny") // Reply message
    }
  }
}

现在,为了模拟我们的Child演员的行为,我们可以使用setAutoPilot方法来定义它在接收特定类型的消息后如何回复。在我们的例子中,假设Child演员接收到字符串类型的消息"Jonny",它将会用一个内容为"Hey there Johnny"的Reply消息进行回复。 首先,我们发送一个内容为"Johnny"的TellName。然后,我们可以断言我们的模拟演员接收到了什么样的消息-childProbe.expectMsg("Johnny")。之后,我们可以断言回复消息-expectMsg("Hey there Johnny")。请注意,回复消息将是"Hey there Johnny"而不是"Hi there Johnny",这对应于我们在模拟的Child演员中定义的更改行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接