Java 8 and Akka 2.12:2.5.16 here. I am trying to write my first (ever) Akka unit test leveraging Akka TestKit and am struggling to apply the principles I see in the (very few) examples I've been able to find online.
My actors:
public class Child extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Init.class, init -> {
int workUnit = workService.doSomeWork();
log.info("Performed work on {}", workUnit);
}).build();
}
}
public class Master extends AbstractActor {
@Inject @Named("CHILD")
private ActorRef child;
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Init.class, init -> {
child.tell(init, self());
}).build();
}
}
Very, very simple. So now I just want to write a unit tests that verifies that when the Master
actor receives an Init
message, that it forwards that message onto its Child
actor. My best attempt thus far:
@RunWith(MockitoJUnitRunner.class)
public class MasterTest {
private ActorSystem actorSystem;
@Before
public void setup() {
actorSystem = ActorSystem.create("test-system");
}
@After
public void teardown() {
Duration duration = Duration.create(10L, TimeUnit.SECONDS);
TestKit.shutdownActorSystem(actorSystem, duration, true);
actorSystem = null;
}
@Test
public void onInit_shouldSendFordwardToChild() {
// Given
TestKit testKit = new TestKit(actorSystem);
ActorRef master = actorSystem.actorOf(Props.create(Master.class, testKit));
// When
master.tell(new Init(), ActorRef.noSender());
// Then
testKit.expectMsg(Init.class);
}
}
When I run this I get:
java.lang.IllegalArgumentException: no matching constructor found on class com.me.myapp.Master for arguments [class akka.testkit.TestKit]
Can someone please help me wire the TestKit
instance into my Master
actor, and also help me figure out how to refactor MasterTest
such that it verifies what I'm trying to accomplish? Thanks in advance!
I figured it out, can't believe how difficult it was to get this to work :-/
In application.conf
:
MyAkkApp {
akka {
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 2553
}
}
}
}
Then:
@RunWith(MockitoJUnitRunner.class)
public class MasterTest extends TestKit {
static ActorSystem actorSystem = ActorSystem.create("MyAkkaApp",
ConfigFactory.load().getConfig("MyAkkaApp"));
static TestProbe child; // The mock child
static ActorRef master;
@BeforeClass
public static void setup() {
child = new TestProbe(actorSystem, "Child");
master = actorSystem.actorOf(Props.create(new Creator<Actor>() {
@Override
public Actor create() throws Exception {
return new Master(child.ref());
}
}));
}
public MasterTest() {
super(actorSystem);
}
@Test
public void onInit_shouldSendFordwardToChild() {
// Given
Init init = new Init();
// When
master.tell(init, super.testActor());
// Then
child.expectMsg(init); // Child should have received it
expectNoMessage(); // Master should not be returning to sender
}
}
Come on Akka folks! Support yields adoption, adoption leads to standardization, standardization means you get to sell 6-figure corporate support licenses.