How to unit test akka actors in Java

I am using akka actors in my java project and I am facing difficulties while writing unit tests for the same. I have written sample code as mentioned below to illustrate my use case.

MainActor is kind of controller actor which receives akka messages but it will not respond anything to the sender. It will send same or different message to some other actor whose ActorRef is created during its initialization.

package sample;

import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;

public class MainActor extends AbstractActor {

    private final ActorSystem actorSystem;
    private final ActorRef subActor1;
    private final ActorRef subActor2;

    public MainActor(ActorSystem actorSystem) {
        this.actorSystem = actorSystem;
        subActor1 = this.actorSystem.actorOf(Props.create(SubActor1.class));
        subActor2 = this.actorSystem.actorOf(Props.create(SubActor2.class));
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(InitSubActor1.class, msg -> {
                    subActor1.tell(msg, self());
                })
                .match(InitSubActor2.class, msg -> {
                    subActor2.tell(msg, self());
                })
                .build();
    }


    public static class InitSubActor1 {
    }

    public static class InitSubActor2 {
    }
}

SubActor1:

package sample;

import akka.actor.AbstractActor;

public class SubActor1 extends AbstractActor {
    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(MainActor.InitSubActor1.class, msg -> {
                    System.out.println("Received InitSubActor1");
                })
                .build();
    }
}

SubAcotor2:

package sample;

import akka.actor.AbstractActor;

public class SubActor2 extends AbstractActor {
    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(MainActor.InitSubActor2.class, msg -> {
                    System.out.println("Received InitSubActor2");
                })
                .build();
    }
}

As shown above, when MainActor receives message of type MainActor.InitSubActor1 , it will send it to SubActor1 and similar thing happens when it receives message of type MainActor.InitSubActor2 .

Now I want to write unit tests for MainActor such that when I send MainActor.InitSubActor1 message from my unit test, I want to assert that MainActor sends it to SubActor1 . I tried to explore TestProbe and found that we can verify actor messages if an actor responds to the sender but I could not find a way to verify the scenario where actor sends message to another actor instead of sender actor.

Below is the unit test which I tried.

package sample;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.TestProbe;
import akka.testkit.javadsl.TestKit;
import org.junit.Test;

import static org.mockito.Mockito.spy;

public class MainActorTest {
    private ActorSystem actorSystem = spy(ActorSystem.create());

    @Test
    public void testActor() {
        new TestKit(actorSystem) {
            {
                final TestProbe probe = TestProbe.apply(actorSystem);
                final Props props = Props.create(MainActor.class, actorSystem);
                final ActorRef mainActor = actorSystem.actorOf(props);
                mainActor.tell(new MainActor.InitSubActor1(), getRef());
                probe.expectMsgClass(MainActor.InitSubActor1.class);
            }
        };
    }
}

This fails since MainActor is not actually replying back to sender. I guess this is not suitable for the use case that I am trying to test. Please let me know if there is any way to write unit tests to verify akka messages sent to different actors.

Note: I can not pass ActorRef of SubActor1 and SubActor2 to the constructor of MainActor

I would extract the creation of subActor1 and subActor2 to methods in MainActor. Then you can replace those in the test with something like:

TestProbe subActor1Probe = TestProbe.create(actorSystem);

class TestMainActor extends MainActor {
  @Override
  protected ActorRef createSubActor1() {
    return subActor1Probe.getRef();
  }
}}

and use subActor1Probe.expect... in to verify.

Note that you should use akka.testkit.javadsl.TestProbe with Java.

By the way, maybe you should create the subActor with this.getContext().actorOf instead, so they have the same lifecycle as the MainActor.

1 Like

Thanks for your response @patriknw. I tried to extend MainActor in my test class and now getting below error. Though I am not creating instance of test class explicitly, it is created while running junit test case.
You cannot create an instance of [sample.MainActorTest] explicitly using the constructor (new)

Please let me know if there is a way to get around this?

You must still create the instance within a Behaviors.setup and pass the ActorContext.

Sorry, this is classic APIs. You have to create the instance via the factory function in a Props.