Testing AKKA Actors With Mockito and FEST-Reflect
Abstract : One of the few frameworks implementing the actor model is AKKA. In fact AKKA is a lot more than just an implementation of the actor model, but within this post we are going to concentrate on combining Mockito, JUnit and FEST-Reflect in order to facilitate actor testing and thus we won't need all the fancy features of AKKA.
Goal : Mocking an instance field within an AKKA actor.
Acknowledgement: My gratitude goes to the open source community and to the following people:
Munish K Gupta - Using AKKA testkit with Java
In order to reach our goal will need the following maven configuration. Note that you need to add the AKKA repository:
<!-- other maven configuration -->
<dependencies>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-testkit</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>akka.repository</id>
<name>Akka Maven Repository</name>
<url>http://repo.akka.io/releases/</url>
</repository>
<!-- other repositories ... -->
</repositories>
The actor which we are going to test looks like this:
import akka.actor.UntypedActor;
import com.honeysoft.business.service.IBusinessService;
import org.springframework.beans.factory.annotation.Autowired;
public class BusinessActor extends UntypedActor {
@Autowired
private IBusinessService businessService;
@Override
public void onReceive(Object message) {
businessService.doBusiness(message);
}
}
As you can see, I'm using Spring framework for dependency injection within the actor, but you are free to choose whatever approach you want to instantiate and assign the business service.
Our goal is quite simple, we have to mock the businessService within our BusinessActor. The difficulty comes from the fact that in a test case we are going to have an ActorRef reference variable to our actor and not a plain Actor reference. This means that our actor is actually nested within an ActorRef reference. As you might already guessed this brings further complications for mocking our businessService reference within the actor instance itself. Or in other words, our businessService is deeply nested and the exact path is actorRef.actorCell.actor.businessService. Luckily for us, since version 1.4 of FEST-Reflect we can play with (deeply) nested fields with a single line of code (not counting the static import). Here is how:
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import com.honeysoft.business.service.IBusinessService;
import com.typesafe.config.ConfigFactory;
import org.fest.reflect.core.Reflection;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import static org.fest.reflect.core.Reflection.*;
import static org.mockito.Matchers.eq;
@RunWith(MockitoJUnitRunner.class)
public class TestBusinessActor {
static ActorSystem akkaSystem = ActorSystem.create("honeysoft-test-system", ConfigFactory.load().getConfig("honeysoft-test-system"));
@Mock
private IBusinessService businessServiceMock;
private ActorRef businessActor;
@Before
public void setup() {
businessActor = akkaSystem.actorOf(new Props(BusinessActor.class));
}
@After
public void clean() {
akkaSystem.stop(businessActor);
}
@Test
public void shouldExecuteBusinessMethod() {
//GIVEN
String businessMessage = "Some business message";
field("actorCell.actor.businessService").ofType(IBusinessService.class)//
.in(businessActor).set(businessServiceMock);
//WHEN
businessActor.tell(businessMessage);
//THEN
Mockito.verify(businessServiceMock, Mockito.times(1))//
.doBusiness(eq(businessMessage));
}
}
As you can see from the highlighted line 43 setting a deeply nested variable is as simple as specifying the path to it. Note that the exact path is businessActor.actorCell.actor.businessService, but the businessActor is already passed to the .in() method and thus as an argument to field() we have only "actorCell.actor.businessService".
To run the test you are also going to need a configuration file named application.conf which I have placed in my src/test/resources directory. The content of application.conf is:
honeysoft-test-system {
akka {
mode = test
event-handlers = ["akka.testkit.TestEventListener"]
loglevel = DEBUG
actor {
debug {
receive = on
autoreceive = on
lifecycle = on
}
}
}
}
Well that was all for this post. Don't hesitate to leave a comment!