Decoration With FEST-Reflect 1.3
Yesterday (3rd of April, 2012) the FEST-Reflect 1.3 (part of FEST libraries) was published on maven central. Since this fact requires a celebration, I've prepared a short tutorial of the new field decorator functionality.
Add the following dependency in your pom.xml to take advantage of FEST-Reflect.
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.3</version>
</dependency>
Example 1: Using FEST-Reflect decorator for testing logging functionality
Let's say we have a simple business service:
package org.honey.service.impl;
import org.honey.service.IBusinessService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BusinessService implements IBusinessService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* {@inheritDoc}
*/
@Override
public void doBusiness() {
logger.info("Hi! This is a business service ...");
}
}
Our goal would be to test that the logger
field is called and still be able to see the printed message on our screen. With the help of FEST-Reflect we can pre-decorate or post-decorate the logger
with another object implementing the org.slf4j.Logger
(I'm going to use a mock object via Mockito). Here is the test:
package org.honey.service.impl;
import static org.fest.reflect.core.Reflection.field;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.honey.service.IBusinessService;
import org.junit.Test;
import org.slf4j.Logger;
public class TestBusinessService {
@Test
public void shouldCallLogger() {
//GIVEN
Logger loggerMock = mock(Logger.class);
IBusinessService service = new BusinessService();
field("logger").ofType(Logger.class).in(service).preDecorateWith(loggerMock);
//WHEN
service.doBusiness();
//THEN
verify(loggerMock, times(1)).info(eq("Hi! This is a business service ..."));
}
}
With the above code, our loggerMock
is going to be called first and then the actual logger
. We can also post-decorate the targeted field (in which case the logger
will be called first and then the loggerMock
):
field("logger").ofType(Logger.class).in(service).postDecorateWith(loggerMock);
In fact we can have both pre-decoration and post-decoration:
field("logger").ofType(Logger.class).in(service)//
.preDecorateWith(loggerMock).postDecorateWith(loggerMock);
However, don't forget to change the verification step (for the test to work) to:
verify(loggerMock, times(2)).info(eq("Hi! This is a business service ..."));
Example 2: Complex decoration
Now let's take a look at what kind of arsenal we have for tackling some more complex scenarios:
Let's upgrade our BusinessService
implementation:
public class BusinessService implements IBusinessService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final INotificationService notificationService;
public BusinessService() {
this.notificationService = new INotificationService() {
@Override
public boolean notify(String recipient) {
// some logic
return false;
}
};
}
/**
* {@inheritDoc}
*/
@Override
public void doBusiness() {
String recipient = "[email protected]";
if (notificationService.notify(recipient)) {
logger.info("Successfully notified {}", recipient);
} else {
logger.error("Could not notifiy {}", recipient);
}
}
}
So what happened? I've added an INotificationService
interface and a simple implementation in the constructor of our BusinessService
. As we can see the implementation of INotificationService.notify()
always returns false, so we can expect that the else
branch in the doBusiness()
method is always taken. What if we want to change this behaviour but without changing the actual INotificationService.notify()
implementation? Here is a test that does exactly that:
public class TestBusinessService {
@Test
public void shouldCallInfoLogger() {
//GIVEN
Logger loggerMock = mock(Logger.class);
INotificationService notificationServiceMock = mock(INotificationService.class);
when(notificationServiceMock.sendNotification(anyString())).thenReturn(true);
IBusinessService service = new BusinessService();
field("logger").ofType(Logger.class).in(service).postDecorateWith(loggerMock);
field("notificationService").ofType(INotificationService.class) //
.in(service).postDecorateWith(notificationServiceMock)//
.returningDecoratorResult();
//WHEN
service.doBusiness();
//THEN
verify(loggerMock, times(1)).info(eq("Successfully notified {}"), eq("[email protected]"));
}
}
Again, we can pre-decorate instead of post-decorate, or we can pre- and post-decorate. Here is how:
field("notificationService").ofType(INotificationService.class) //
.in(service).preDecorateWith(notificationServiceMock1)//
.returningDecoratorResult()// taking the result from notificationServiceMock1
.postDecorateWith(notificationServiceMock2);
Ok, but what if our notificationServiceMock
throws an exception? Well, we can silence any RuntimeException
like this:
field("notificationService").ofType(INotificationService.class) //
.in(service).postDecorateWith(notificationServiceMock).returningDecoratorResult()//
.ignoringDecoratorExceptions(); // ignores any RuntimeException
Or a specific exception like this:
field("notificationService").ofType(INotificationService.class) //
.in(service).postDecorateWith(notificationServiceMock).returningDecoratorResult()//
.ignoringDecoratorExceptionsOfType(MyCoolException.class); // ignores only MyCoolException