How to test Akka Actor functionality by mocking one or more methods in it

ses picture ses · May 23, 2013 · Viewed 13.4k times · Source

I'm interested to know about how to test Akka Actor functionality, by mocking some methods (substitute real object's/actor's method implementation by mocked one) in Actor.

I use akka.testkit.TestActorRef;

Also: I tried to use SpyingProducer but it is not clear how to use it. (like I if I created actor inside its implementation it would be the same I have now). The google search result about that is not very verbose.

I use powemockito and java. But It does not matter. I would be interested to know how to do it in principle with any language with any framework

(so if you do not know how power/mockito works just provide your code.. (please) or complete idea about how you would do it with your tools you know.)

So, let's say we have an Actor to test:

package example.formock;

import akka.actor.UntypedActor;

public class ToBeTestedActor extends UntypedActor {

    @Override
    public void onReceive(Object message) throws Exception {

        if (message instanceof String) {
            getSender().tell( getHelloMessage((String) message), getSelf());
        }

    }

    String getHelloMessage(String initMessage) { // this was created for test purposes (for testing mocking/spy capabilities). Look at the test
        return "Hello, " + initMessage;
    }

}

And in our test we want to substitute getHelloMessage() returning something else.

This is my try:

package example.formock;

import akka.testkit.TestActorRef;
...

@RunWith(PowerMockRunner.class)
@PrepareForTest(ToBeTestedActor.class)
public class ToBeTestedActorTest {

    static final Timeout timeout = new Timeout(Duration.create(5, "seconds"));

    @Test
    public void getHelloMessage() {

        final ActorSystem system = ActorSystem.create("system");

        // given
        final TestActorRef<ToBeTestedActor> actorRef = TestActorRef.create(
                system,
                Props.create(ToBeTestedActor.class),
                "toBeTestedActor");

        // First try:
        ToBeTestedActor actorSpy = PowerMockito.spy(actorRef.underlyingActor());
        // change functionality
        PowerMockito.when(actorSpy.getHelloMessage (anyString())).thenReturn("nothing"); // <- expecting result   


        try {

           // when
           Future<Object> future = Patterns.ask(actorRef, "Bob", timeout);
           // then
           assertTrue(future.isCompleted());

            // when
           String resultMessage = (String) Await.result(future, Duration.Zero());
            // then
           assertEquals("nothing", resultMessage);  // FAIL HERE

        } catch (Exception e) {
           fail("ops");
        }
    }
}

Result:

org.junit.ComparisonFailure: 
Expected :nothing
Actual   :Hello, Bob

Answer

Douglas Rapp picture Douglas Rapp · Oct 29, 2013

Akka has a class AutoPilot that is basically a general mock for actors, with the ability to respond to messages and assert that messages were sent. http://doc.akka.io/docs/akka/snapshot/java/testing.html

Here's the java example for that page. You create a probe, set an auto-pilot that can respond to messages, and get an ActorRef from it that you can substitute in for your real actor.

new JavaTestKit(system) {{
  final JavaTestKit probe = new JavaTestKit(system);
  // install auto-pilot
  probe.setAutoPilot(new TestActor.AutoPilot() {
    public AutoPilot run(ActorRef sender, Object msg) {
      sender.tell(msg, ActorRef.noSender());
      return noAutoPilot();
    }
  });
  // first one is replied to directly ...
  probe.getRef().tell("hello", getRef());
  expectMsgEquals("hello");
  // ... but then the auto-pilot switched itself off
  probe.getRef().tell("world", getRef());
  expectNoMsg();
}};