簡體   English   中英

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

[英]Akka. How to mock child actors in java?

假設我有一個自己創建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 {

    }
}

我怎么能嘲笑這個孩子的演員呢? 谷歌沒有給我任何合理的結果。 Akka的文檔告訴我,創建演員是一種很好的做法。 但是,如果我甚至無法測試我的演員,我怎么能遵循這種做法呢?

我使用類似問題的答案中描述的探針:

如何模擬兒童演員測試Akka系統?

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

以下代碼將用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發送給Child actor。 收到消息后, Child actor將通過向其發件人發送包含內容的Reply消息來響應 - 即“Hi there Joe”。 現在,這是一個測試:

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收到String類型的消息“Jonny”,它將回復一條Reply消息,其內容為“Hey there Johnny”。 首先,我們發送TellName的內容為“Johnny”。 然后我們可以斷言我們的模擬演員收到的消息 - childProbe.expectMsg("Johnny") 之后我們可以斷言回復消息 - expectMsg("Hey there Johnny") 請注意,回復消息將是“Hey there Johnny”而不是“Hi there Johnny”,它對應於我們在模擬Child actor中定義的改變行為。

您可以采用幾種方法來模擬Child actor。 其中之一是從父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();
  }
}

現在在您的測試中,您可以使用probe actor引用來測試應該發送給Child actor的消息,即probe將充當Child actor mock:

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”消息。

希望能幫助到你。 有關給定方法的更多信息,請點擊此處

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM