RSS

Decoration with FEST-Reflect 1.3

04 Apr

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 = "some@one.com";

    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("some@one.com"));
  }
}

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
Advertisements
 
1 Comment

Posted by on April 4, 2012 in FEST-Reflect, Java, Uncategorized

 

One response to “Decoration with FEST-Reflect 1.3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: