I have an actor FooActor
that gets passed the Props
to instantiate several BarActor
s and send BarMessage
s to it. The code works, but I am having a hard time writing tests for it. The added restriction is that I can only use Java code in this app, no Scala code.
After several attempts, this seems to be my best effort so far:
@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();
}};
}
It all seems to make sense to me, but even though I can see messages being sent to newly created Bar
instances, the first assertion fails. What am I doing wrong?
Update:
The thing that makes this different from the Akka documentation example, is that I don't want to pass an existing actor that receives the message. Instead, I want to pass the Props
that is used to create instances of child actors instead. In the test, I want my probe
to receive messages to those newly created actors. This is why I added the Props.create
construct that should return the same probe actor all the time. Just now I saw this comment in the Creator.create
API:
This method must return a different instance upon every call.
So this will obviously not work, as that it precisely what I want. So my general question remains: how can I test for messages being sent to newly created child actors?
You're trying to "cheat" on how the child actors are initialized (by passing probeRef
) to have flexibility in testing them, but the problem is that the even Creator
is generic, used in standard context getContext().actorOf()
, cannot return ActorRef
as a result of the create
method. The contract needs always be as follow Creator<T extends Actor>
.
Please have a look at ActorCreationTest
I might be wrong because, I don't know how your FooActor
is implemented but if you will use stadard pattern getContext().actorOf()
default Akka akka.actor.CreatorConsumer
will not accept your 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
Instead of returning ActorRef
to probe you may try return anonymous forwarder 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 {
}
}
I think something like this should work for your case.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With