Mockito is what I would like to consider a next-generation mocking tool, with support for both mocking and stubbing. Mockito fully and naturally uses recent language enhancements in Java, such as generics, static imports and annotations, to make the tool easy to use. Writing clean and elegant test code that is easy to understand can actually be pretty simple. TheMockito API is straightforward and well designed, the need for infrastructure code is kept to a minimum. If you are an EasyMock user, this comparison may be helpful when following along in the code below: http://code.google.com/p/mockito/wiki/MockitoVSEasyMock
Let's look at a few examples!
Stub an interface
A nice property of systems built using dependency injection is that you get loosely coupled systems with well-defined interfaces that are easy to stub for testing. It is easy enough to provide stub implementations of these interface directly in the test classes, e.g. as anonymous implementations or inner classes. But despite this, I have found myself more and more starting to rely on Mockito for these situations, it's fewer lines of code, and very convenient!import static org.mockito.Mockito.mock;Now, as an example, we can use our ClientRepository instance to test a service class:
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import static org.junit.Assert.assertEquals;
[...]
ClientRepository repository = mock(ClientRepository.class);
ClientId clientId = new ClientId(123);
Client client = new Client(clientId, new Name("Test", "User"));
when(repository.findById(clientId)).thenReturn(client);
FancyService service = new FancyServiceImpl();When stubbing like this we are usually not that interested in verifying behaviour of the stub. But if we, for some reason, would like to explicitly make sure that the method
service.setRepository(repository);
assertEquals(client, service.findClientAndDoSomethingTrulyAwesome(123));
findById(...)
was called, we can do this with the following line of code:verify(repository).findById(clientId);
Stubbing concrete classes
Now and then you encounter API:s that were not as designed for testing that you could wish for. In these cases, the possibilities of stubbing concrete classes can be very helpful.Restlet
Restlet is a Java framework for creating RESTful web service. Reslet consists of quite a big API, and the authors have, among other things, decide to create their own implementation of things likeRequest
, Response
, Status.SUCCESS_OK
(HTTP response 200) etc. Unfortunately, many parts of the API consists of concrete classes, instead of interfaces. One downside of this is that it is sometimes hard to write tests for code that uses the Restlet APIs.Stubbing the concrete class
org.restlet.data.Response
is easily done in the same way we stubbed the ClientRepository
interface above:import static org.mockito.Mockito.*;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.resource.Representation;
[...]
Response response = mock(Response.class);
Representation entity = mock(Representation.class);
when(response.getStatus()).thenReturn(Status.SUCCESS_OK);
when(response.getEntity()).thenReturn(entity);
Quartz
Quartz is a scheduling component that is supported and also used by many popular application development frameworks and application servers, including Spring and Seam. When using Quartz you define jobs that execute according to a schedule. Every time a job is triggered a new instance of the job class is created, and executed. If state is to be saved between job executions it has to be stored in some kind data structure outside of the job. Quartz makes a context available,org.quartz.JobExecutionContext
, for this and other purposes. The context is handed to every newly created job instance. The context can, a bit simplified, be viewed as Map
where the job can store and retrieve data.If we would like to make data available to a job or read the result from a job execution we have to create an instance of
JobExecutionContext
and hand to our job. Creating this instance is however quite complicated, and since Quartz also tends to favor concrete classes over interfaces, it makes it hard for us to provide our own implementation. Fortunately, Mockito (or another mocking tool that can mock concrete classes) can help us out here as well!Use Mockito to setup the context and set a fictional
indexCount
parameter as input value to the job:import static org.mockito.Mockito.*;Now we can use the context in our test:
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
[...]
JobExecutionContext ctx = mock(JobExecutionContext.class);
JobDetail detail = mock(JobDetail.class);
JobDataMap map = new JobDataMap();
map.put("indexCount", 145);
when(detail.getJobDataMap()).thenReturn(map);
when(ctx.getJobDetail()).thenReturn(detail);
IndexUpdaterJob job = new IndexUpdater();And verify that the index was updated:
job.execute(ctx);
map = ctx.getJobDetail().getJobDataMap();
assertEquals(146, map.get("indexCount"));
No comments:
Post a Comment