[英]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的文檔告訴我,創建演員是一種很好的做法。 但是,如果我甚至無法測試我的演員,我怎么能遵循這種做法呢?
我使用類似問題的答案中描述的探針:
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.