阿卡。如何在java中模拟子actor?

时间:2016-03-13 15:59:08

标签: java akka

让我们说我有一个自己创作演员的父演员。

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 {

    }
}

我怎么能模仿这个孩子的演员?谷歌没有给我任何合理的结果。 Akka的文档告诉我,创建演员是一种很好的做法。但是,如果我甚至不能测试我的演员,我怎么能遵循这种做法呢?

3 个答案:

答案 0 :(得分:1)

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

How to mock child Actors for testing an Akka system?

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

答案 1 :(得分:0)

以下代码将用Scala编写,但我想这同样适用于Java。答案是您可以使用TestProbe来模拟您的孩子演员。我们来看这个例子:

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 actor,它向TellName actor发送消息Child。收到消息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 actor的行为,我们可以使用setAutoPilot方法来定义在收到特定类型的消息后它将如何回复。在我们的例子中,假设Child actor收到字符串类型的消息“Jonny”,它将回复一条内容为“Hey there Johnny”的Reply消息。 首先,我们发送TellName内容为“Johnny”。然后我们可以断言我们的模拟演员收到的消息 - childProbe.expectMsg("Johnny")。之后我们可以断言回复消息 - expectMsg("Hey there Johnny")。请注意,回复消息将是“Hey there Johnny”而不是“Hi there Johnny”,它对应于我们在模拟的Child actor中定义的改变行为。

答案 2 :(得分:0)

可以使用several approaches来嘲笑儿童演员。其中之一是外部化从父演员创建孩子的代码。

要做到这一点,您需要重写您的父级演员,并向其传递一个可以创建您的子级演员的函数:

注意:由于deprecation in version 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”消息。

希望有帮助。有关给定方法的更多信息,请参见here