如何测试Actor Foo向新创建的儿童演员Bar发送消息?

时间:2014-09-24 12:55:25

标签: java akka

我有一个演员FooActor通过Props来实例化几个BarActor并向其发送BarMessage。代码有效,但我很难为它编写测试。增加的限制是我只能在这个应用程序中使用Java代码,没有Scala代码。

经过多次尝试,这似乎是我迄今为止的最大努力:

@Test
public void testJavaTestKit() {

    new JavaTestKit(system) {{
        JavaTestKit probe = new JavaTestKit(system);

        // pretending that the probe is the receiving Bar, by returning it in the Props
        Props barActorProps = Props.create(BarActor.class, new Creator() {
            @Override
            public Object create() {
                return probe.getRef();
            }
        });
        Props props = Props.create(FooActor.class, barActorProps);
        ActorRef subject = system.actorOf(props);

        Object msg = // basically irrelevant, will trigger Bar instantiation and message sending

        subject.tell(msg, probe.getRef());

        expectMsgClass(Bar.BarMessage.class);
        expectNoMsg();
    }};
}

这一切似乎对我有意义,但即使我可以看到消息被发送到新创建的Bar实例,第一个断言也会失败。我究竟做错了什么?

更新

使这与Akka文档示例不同的是,我不想传递接收消息的现有actor。相反,我想传递用于创建子actor实例的Props。在测试中,我希望我的probe接收到新创建的actor的消息。这就是我添加Props.create构造的原因,它应该始终返回相同的探测器。刚才我在Creator.create API中看到了这条评论:

  

此方法必须在每次调用时返回不同的实例。

所以这显然不会起作用,因为它正是我想要的。所以我的一般问题仍然是:如何测试发送给新创建的子actor的消息?

2 个答案:

答案 0 :(得分:4)

你试图“欺骗”儿童演员的初始化方式(传递probeRef)以便灵活地测试它们,但问题是偶数Creator是通用的,使用过的在标准上下文getContext().actorOf()中,ActorRef方法无法返回create。合同需要始终如下Creator<T extends Actor>

请查看ActorCreationTest

我可能错了,因为我不知道您的FooActor是如何实施的,但如果您使用标准模式getContext().actorOf(),则默认的Akka akka.actor.CreatorConsumer将不接受您的Consumer }。

caused by: java.lang.ClassCastException: akka.actor.RepointableActorRef cannot be cast to akka.actor.Actor
    at akka.actor.CreatorConsumer.produce(Props.scala:335)
    at akka.actor.Props.newActor(Props.scala:252)
    at akka.actor.ActorCell.newActor(ActorCell.scala:552)
    at akka.actor.ActorCell.create(ActorCell.scala:578)
    ... 9 more

您可以尝试返回匿名转发器ActorRef而不是返回Actor进行探测吗?

import akka.actor.*;
import akka.japi.Creator;
import akka.testkit.JavaTestKit;
import org.junit.Test;

public class ActorTest {

    @Test
    public void testJavaTestKit() {
        ActorSystem system = ActorSystem.create("Acceptor");

        new JavaTestKit(system) {{
            JavaTestKit probe = new JavaTestKit(system);

            Creator creator = () -> new UntypedActor() {
              @Override
              public void onReceive(Object message) throws Exception {
                  probe.getRef().tell(message, sender());
              }
            };


            ActorRef subject = system.actorOf(Props.create(FooActor.class, creator));

            subject.tell(new Bar().new BarMessage(), probe.getRef());
            probe.expectMsgClass(Bar.BarMessage.class);


        }};
    }

}


class BarActor extends UntypedActor {

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

    }
}

class FooActor extends UntypedActor {

    ActorRef barRef;

    public static Props props(final Creator creator) {
        return Props.create(FooActor.class, () -> new FooActor(creator));
    }
    public FooActor(Creator creator) {
        barRef = getContext().actorOf(Props.create(creator),"bar");
    }

    @Override
    public void onReceive(Object message) throws Exception {
        barRef.tell(message,sender());
    }
}

class Bar {
    class BarMessage {

    }
}

答案 1 :(得分:1)

我认为这样的事情应该适合你的情况。

class ActorSpec extends Specification {
    @Shared
    private ActorSystem system
    @Shared
    private ActorRef actorRef
   def setupSpec() {
        system = ActorSystem.create("test-system",ConfigFactory.load("application.conf"))
    }
   def "Verify message received"() {
     given:
            JavaTestKit probe = new JavaTestKit(system)
            final Props props = Props.create(BarActor.class)
            actorRef = system.actorOf(props)
        when:
            actorRef.tell(new BarMessage(), probe.getRef())
        then:
            probe.expectMsgClass(Bar.BarMessage.class);
    }
 def cleanupSpec() {
        JavaTestKit.shutdownActorSystem(system)
        system = null
    }