Akka. How to mock child actors in java?

Viewed 1364

Let's say I have a parent actor who creates Actor by himself.

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 {

    }
}

How can I mock this child Actor? Google didn't give me any reasonable results. I've been told by Akka's documentation that creating an actor is a good practice. But how can I follow this practice if I can't even test my actors?

3 Answers

There are several approaches you can take in order to mock the Child actor. One of them is to externalize code for child creation from parent actor.

In order to do it, you need to rewrite your Parent actor and pass it a function that will create your Child actor:

Note: we will use AbstractActor instead of UntypedActor due to deprecation in version 2.5.0.

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

Your Child actor will stay the same:

public class Child extends AbstractActor {

  @Override
  public Receive createReceive() {
    return receiveBuilder()
        .matchEquals("ping", s -> getSender().tell("pong", getSelf()))
        .build();
  }
}

Now in your test you can use probe actor reference to test the message that should be sent to Child actor i.e. probe will act as 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");
  }
}

So, instead of using (would be used in real application code):

Function<ActorRefFactory, ActorRef> childCreator = arf -> arf
                                      .actorOf(Props.create(Child.class));

we are going to use:

Function<ActorRefFactory, ActorRef> childCreator = arf -> probe.getRef();

and check if probe received a "ping" message.

Hope it helps. More info about given approach can be found here.

Related