如何测试Actor Foo是否向新创建的子Actor Bar发送消息?

6

我有一个名为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接收那些新创建的actors的消息。这就是为什么我添加了Props.create构造函数,它应该始终返回相同的probe actor。刚才我在Creator.create API中看到了这个注释:
“此方法必须在每次调用时返回不同的实例。”
显然,这样不起作用,因为这正是我想要的。所以我的一般问题仍然存在:如何测试发送给新创建的子actor的消息?

2
你期望接收消息的位置是 JavaTestKit ActorRef 吗?如果是,你需要调用 probe.expectMsgClass 方法。目前你正在测试方法中对匿名的 JavaTestKit 进行断言。除非你的测试 "subject" 回复的是 Bar.BarMessage.class,否则该断言将始终失败。 - nickebbitt
2
解决方案可能就是将断言更改为 probe.expectMsgClass(Bar.BarMessage.class); - nickebbitt
谢谢你的回答。那看起来很有前途,可能已经让我离解决方案更近了,尽管此时它还不起作用。 - Jeroen Kransen
没问题。提供 FooActor.class 的代码可能会帮助您获得所需的解决方案。当我最初开始使用Java测试Akka Actors时,我遵循了这里描述的模式 [github-link](https://gist.github.com/jconwell?direction=asc&sort=created),特别是在您的情况下,ForwarderActor听起来就像是您需要的东西。 - nickebbitt
@JeroenKransen 有没有可能让我们看到你的FooActor实现呢? - hicolour
2个回答

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

你可以尝试返回一个匿名的转发者 Actor,而不是返回 ActorRef 作为探测器。

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
我认为像这样的东西对你的情况应该是有效的。
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
    }

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