Let’s check out some testing code which tests out the following different set of actors
- Echo Actor – responds back with whatever is passed to the actor
- Forwarding Actor – forwards the message to another actor
- Sequencing Actor – replies back in a series of messages but assuming we are interested in one
- Filtering Actor – replies back for certain messages and ignores the others
- Boom Actor – throws an exception when a message is send
- Supervisor Actor – manages an another worker actor, and based on the exception thrown by the worker actor, applies the appropriate supervisor strategy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Test | |
public void testEchoActor() { | |
ActorRef echoActorRef = _system.actorOf(new Props(EchoActor.class)); | |
// pass the reference to implicit sender testActor() otherwise | |
// message end up in dead mailbox | |
echoActorRef.tell("Hi there", super.testActor()); | |
expectMsg("Hi there"); | |
} | |
@Test | |
public void testForwardingActor() { | |
ActorRef forwardingActorRef = _system.actorOf(new Props( | |
new UntypedActorFactory() { | |
public UntypedActor create() { | |
return new ForwardingActor(testActor()); | |
} | |
})); | |
// pass the reference to implicit sender testActor() otherwise | |
// message end up in dead mailbox | |
forwardingActorRef.tell("test message", super.testActor()); | |
expectMsg("test message"); | |
} | |
@Test | |
public void testSequencingActor() { | |
final List<Integer> headList = new ArrayList<Integer>(); | |
final List<Integer> tailList = new ArrayList<Integer>(); | |
int randomHead = new Random().nextInt(6); | |
int randomTail = new Random().nextInt(10); | |
for (int i = 0; i < randomHead; i++) | |
headList.add(i); | |
for (int i = 1; i < randomTail; i++) | |
tailList.add(i); | |
TestActorRef<SequencingActor> sequencingActorRef = TestActorRef.apply( | |
new Props(new UntypedActorFactory() { | |
public UntypedActor create() { | |
return new SequencingActor(testActor(), headList, | |
tailList); | |
} | |
}), _system); | |
// pass the reference to implicit sender testActor() otherwise | |
// message end up in dead mailbox | |
sequencingActorRef.tell("do something", super.testActor()); | |
for (Integer value : headList) { | |
expectMsgClass(Integer.class); | |
} | |
expectMsg("do something"); | |
for (Integer value : tailList) { | |
expectMsgClass(Integer.class); | |
} | |
} | |
@Test | |
public void testFilteringActor() { | |
TestActorRef<FilteringActor> filteringActorRef = TestActorRef.apply( | |
new Props(new UntypedActorFactory() { | |
public UntypedActor create() { | |
return new FilteringActor(testActor()); | |
} | |
}), _system); | |
// pass the reference to implicit sender testActor() otherwise | |
// message end up in dead mailbox | |
// first test | |
filteringActorRef.tell("test message", super.testActor()); | |
expectMsg("test message"); | |
// second test | |
filteringActorRef.tell(1, super.testActor()); | |
expectNoMsg(); | |
} | |
/** | |
* if you want to test whether the Supervisor strategy is working fine | |
*/ | |
@Test | |
public void testSupervisorStrategy1() throws Exception { | |
final TestActorRef<SupervisorActor> supervisorActorRef1 = TestActorRef | |
.apply(new Props(new UntypedActorFactory() { | |
public UntypedActor create() { | |
return new SupervisorActor(); | |
} | |
}), "supervisor1", _system); | |
Duration timeout = Duration.parse("5 second"); | |
// register the BoomActor with the Supervisor | |
final ActorRef child = (ActorRef) Await.result( | |
ask(supervisorActorRef1, new Props(BoomActor.class), 5000), | |
timeout); | |
child.tell(123); | |
Assert.assertFalse(child.isTerminated()); | |
} | |
@Test | |
public void testSupervisorStrategy2() throws Exception { | |
TestActorRef<SupervisorActor> supervisorActorRef2 = TestActorRef.apply( | |
new Props(new UntypedActorFactory() { | |
public UntypedActor create() { | |
return new SupervisorActor(); | |
} | |
}), "supervisor2", _system); | |
final TestProbe probe = new TestProbe(_system); | |
// register the BoomActor with the Supervisor | |
final ActorRef child = (ActorRef) Await.result( | |
ask(supervisorActorRef2, new Props(BoomActor.class), 5000), | |
Duration.parse("5 second")); | |
probe.watch(child); | |
// second check | |
child.tell("do something"); | |
probe.expectMsg(new Terminated(child)); | |
} | |
@Test | |
public void testBoomActor() { | |
final TestActorRef child = TestActorRef.apply( | |
new Props(BoomActor.class), _system); | |
try { | |
child.receive("do something"); | |
//should not reach here | |
Assert.assertTrue(false); | |
} catch (IllegalArgumentException e) { | |
Assert.assertEquals(e.getMessage(), "boom!"); | |
} | |
} |
I moved your two tests from src/main/java to src/test/java and get the following error:
ReplyDeletecom.typesafe.config.ConfigException$Missing: No configuration setting found for key 'TestSys'
Is there supposed to be an application.conf in the classpath?
Yes, there is one and you will find the same under src/main/resource.
ReplyDeleteI forgot to say a big thank you for publishing these examples.
ReplyDeleteMy attempted at doing a git push failed, so here are the changes I made:
ReplyDelete- Moved all of your test classes to src/test/java/org.../actors
- Renamed UnitTestExample.java to ExampleUnitTest.java
- Renamed resource/ to resources/
Now I can run mvn clean install and all tests are executed.
Robin
Thanks Robin. I will make the changes and push them out.
ReplyDeleteI think there's are an issue preventing testSupervisorStrategy1() from actually testing what it wants to test: While child.tell(123) will cause the "child" actor to eventually blow up and be restarted by its supervisor, Akka gives no guarantee as to when this happens.
ReplyDeleteThus the final assertFalse(child.isTerminated()) can potentially be executed before the child has reacted to the 123 message, causing isTerminated() to yield false regardless of whether the supervision strategy is working properly or not.
When you make use of TestActorRef, it sets the dispatcher to CallingThreadDispatcher and sets the receiveTimeout to None
ReplyDelete