Java 8 and Akka (Java API) 2.12:2.5.16 here. I have the following message:
public class SomeMessage {
private int anotherNum;
public SomeMessage(int anotherNum) {
this.anotherNum = anotherNum;
}
public int getAnotherNum() {
return anotherNum;
}
public void setAnotherNum(int anotherNum) {
this.anotherNum = anotherNum;
}
}
And the following actor:
public class TestActor extends AbstractActor {
private Integer number;
public TestActor(Integer number) {
this.number = number;
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(message -> {
if (message instanceof SomeMessage) {
SomeMessage someMessage = (SomeMessage) message;
System.out.println("someMessage contains = " + someMessage.getAnotherNum());
someMessage.setAnotherNum(number);
}
}).build();
}
}
And the following unit test:
@RunWith(MockitoJUnitRunner.class)
public class TestActorTest {
static ActorSystem actorSystem;
@BeforeClass
public static void setup() {
actorSystem = ActorSystem.create();
}
@AfterClass
public static void teardown() {
TestKit.shutdownActorSystem(actorSystem, Duration.create("10 seconds"), true);
actorSystem = null;
}
@Test
public void should_alter_some_message() {
// given
ActorRef testActor = actorSystem.actorOf(Props.create(TestActor.class, 10), "test.actor");
SomeMessage someMessage = new SomeMessage(5);
// when
testActor.tell(someMessage, ActorRef.noSender());
// then
assertEquals(10, someMessage.getAnotherNum());
}
}
So all I'm trying to verify is that TestActor
does in fact receive a SomeMessage
and that it alters its internal state.
When I run this unit test, it fails and is as if the actor never receives the message:
java.lang.AssertionError:
Expected :10
Actual :5
<Click to see difference>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
<rest of trace omitted for brevity>
[INFO] [01/30/2019 12:50:26.780] [default-akka.actor.default-dispatcher-2] [akka://default/user/test.actor] Message [myapp.actors.core.SomeMessage] without sender to Actor[akka://default/user/test.actor#2008219661] was not delivered. [1] dead letters encountered. If this is not an expected behavior, then [Actor[akka://default/user/test.actor#2008219661]] may have terminated unexpectedly, This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
But when I modify the test method and introduce a Thread.sleep(5000)
into it (after the tell(...)
) it passes with flying colors:
@Test
public void should_alter_some_message() throws InterruptedException {
// given
ActorRef testActor = actorSystem.actorOf(Props.create(TestActor.class, null, 10), "test.actor");
SomeMessage someMessage = new SomeMessage(5);
// when
testActor.tell(someMessage, ActorRef.noSender());
Thread.sleep(5000);
// then
assertEquals(10, someMessage.getAnotherNum());
}
What's going on here?! Obviously I don't want my actor tests littered with sleeps
, so what am I doing wrong here and what is the fix? Thanks in advance!