Jan Amoyo

on software development and possibly other things

Showing posts with label junit. Show all posts
Showing posts with label junit. Show all posts

Static Methods and Unit Testing

Overview

We all know that Interfaces allow us to write loosely-coupled components, and that this "loose-coupledness" comes-in handy during unit testing. Because we can separate the implementation from the method signatures, Interfaces allow us to mock implementations of our dependencies.

Mocking dependencies is important during unit testing because it allows us to isolate the components we are testing from their dependencies -- this means that incorrect behavior from any dependency will not affect the results of our tests.

Consider the following class:
public class Component {
  private Dependency dependency

  public Component(Dependency dependency) {
    this.dependency = dependency
  }

  public void componentMethod() {
    int imporantValue = dependency.getImportantValue();
    // use 'imporantValue' to calculate stuff
  }
}
And the following Interface:
public interface Dependency {
  int getImportantValue();
}
Whose default implementation is defined as:
public class DefaultDependency implements Dependency {
  public int getImportantValue() {
    int value = // fetch imporant value from external service
    return value;
  }
}
In order for us to test componentMethod(), we'd have to setup our unit test environment to allow connections to the external service; and if this external service fails, our unit test would fail as well.

Mocking the dependency allows us to execute our unit test without the need for an external service:
public class MockedDependency implements Dependency {
  public int getImportantValue() {
    // mocked value
    return 1;
  }
}
Because we are providing a simple and consistent implementation, we are assured that our dependency always returns the correct value and therefore would not compromise the results of our tests.

Mockito

Mockito is a mocking framework which simplifies mocking. It allows us to mock dependencies with very little code and to customize our mocked behavior depending on our testing scenario:
// without mocking
Component component = new Component(new DefaultDependency());

// with mocking
Component component = new Component(new MockedDependency());

// with Mockito
Dependency dependency = Mockito.mock(Dependency.class);
Mockito.when(dependency.getImportantValue()).thenReturn(1);
Component component = new Component(dependency);
A great thing about Mockito is that it also supports mocking concrete classes.

Note that in the above examples, we are assuming that unit tests were also written for DefaultDependency. Without this, we cannot guarantee the overall correctness of the application.

Static Methods

The point that I'm trying to make is that abstract methods provide us the flexibility to mock our implementation. Using mocking frameworks such as Mockito, we can even extend this flexibility to concrete methods. The same cannot be said for static methods.

Because they are associated with the class rather than an instance, static methods cannot be overridden. In fact, most static utility classes in Java are marked as final. And because they cannot be overridden, it is impossible to mock static method implementations.

Suppose that in our previous example, Dependency was implemented as a static utility class:
public class Component {
  public void componentMethod() {
    int imporantValue = Dependency.getImportantValue();
    // use 'imporantValue' to calculate stuff
  }
}
public final class Dependency {
  public static int getImportantValue() {
    int value = // fetch imporant value from external service
    return value;
  }
}
Because we cannot override getImportantValue() with a mocked implementation, there is simply no way for us to test componentMethod() without requiring a connection to the external service.

Singletons

You might have come across statements saying that "Singletons are evil". That is partly true depending on how you create and use Singletons.

Suppose that in our previous example, Dependency was implemented and used as a classic Singleton:
public class Component {
  public void componentMethod() {
    // classic use of Singleton
    int imporantValue = Dependency.getInstance().getImportantValue();
    // use 'imporantValue' to calculate stuff
  }
}
public final class Dependency {
  private static final Dependency instance = new Dependency();

  private Dependency() {}

  public static Dependency getInstance() {
    return instance;
  }

  public int getImportantValue() {
    int value = // fetch imporant value from external service
    return value;
  }
}
Because getInstance() is a static method, all the evils associated with static methods apply to the Singleton as well (also applicable to Singletons implemented as enums).

Obviously not all Singletons are evil. With slight modifications, we can fix what's evil about our previous implementation:
public interface Dependency {
  int getImportantValue();
}
public final class DefaultDependency implements Dependency {
  private static final Dependency instance = new DefaultDependency();

  private DefaultDependency() {}

  public static Dependency getInstance() {
    return instance;
  }

  public int getImportantValue() {
    int value = // fetch imporant value from external service
    return value;
  }
}
By making Dependency an Interface and only applying the Singleton pattern to its default implementation, our Component class can be implemented exactly as the original version:
public class Component {
  private Dependency dependency;

  public Component(Dependency dependency) {
    this.dependency = dependency
  }

  public void componentMethod() {
    int imporantValue = dependency.getImportantValue();
    // use 'imporantValue' to calculate stuff
  }
}
Once again making it unit-testable:
// without mocking, acquire Singleton instance
Component component = new Component(DefaultDependency.getInstance());

// with mocking
Component component = new Component(new MockedDependency());

// with Mockito
Dependency dependency = Mockito.mock(Dependency.class);
Mockito.when(dependency.getImportantValue()).thenReturn(1);
Component component = new Component(dependency);
The above is an example of Inversion of Control (IoC). Instead of acquiring the Dependency instance from the Component class, we let the container decide which instance of Dependency to assign to Component.

Can you think of another popular pattern with a tendency to have the same issues described above? Hint: it's called Service Locator.

Conclusion

Static methods have their use. But because of their impact to unit testing, caution must be applied before using them. Personally, I limit my use of static methods to utility classes having small and unchanging logic (example: org.apache.commons.io.IOUtils).

If required to use static factory methods, applying Inversion of Control should help enforce unit-testablity.

Update: If you really need use static methods on your dependencies, you can use PowerMock.
PowerMock.mockStatic(Foo.class);
expect(Foo.bar()).andReturn("foo bar");
Check this page for more information.

Testing Spring MVC Annotated JSON Controllers from JUnit

Update: While I still recommend this post for good reading, please refer to this post for a better approach at doing this.

My previous post explained how we can use AnnotationMethodHandlerAdapter to test annotations applied to Spring MVC Controllers. This post will attempt to explain how we can reuse the classes introduced in the previous post to test Spring Controllers that return a JSON response.

The @ResponseBody annotation allows Spring Controllers to define the contents of an HTTP response.

For this example, we assume that Spring MVC is configured to represent the contents of the HTTP response as JSON.

Let's say we have a controller which returns user information from a GET request:
@Controller
public class MyJsonController {
  private final UserService userService = new UserService();

  @RequestMapping(value = "/user/{username}", method = RequestMethod.GET)
  public @ResponseBody User getUser(@PathVariable String username) {
    return userService.getUser(username);
  }

  // Mocked for illustration purposes
  private static class UserService {
    public User getUser(String username) {
      User user = new User(username);
      user.setFirstName("Jan");
      user.setLastName("Amoyo");
      return user;
    }
  }
}
This controller returns a User object serialized within the body of the HTTP response (in our case, as JSON). Because it doesn't return an instance of  ModelAndView, we cannot use the previously introduced AbstractControllerTest to test the output.

In order to test the JSON response, we need to assign an appropriate HttpMessageConverter to the AnnotationMethodHandlerAdapter defined in AbstractControllerTest. The one we need is MappingJacksonHttpMessageConverter.

Extending AbstractControllerTest

We will create a new class called AbstractJsonControllerTest and extend from AbstractControllerTest. Here, we override the parent's constructor so that we can assign MappingJacksonHttpMessageConverter to the AnnotationMethodHandlerAdapter. We also add various convenience methods to help process JSON.
@Ignore("abstract test case")
public abstract class AbstractJsonControllerTest&ltT&gt extends AbstractControllerTest&ltT&gt {
  private final ObjectMapper mapper;

  public AbstractJsonControllerTest(T controller) {
    super(controller);
    mapper = new ObjectMapper();
    MappingJacksonHttpMessageConverter jacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
    MappingJacksonHttpMessageConverter[] messageConverters = { jacksonHttpMessageConverter };
    getHandlerAdapter().setMessageConverters(messageConverters);
  }

  protected List&ltMap&ltString, Object&gt&gt convertJsonArrayHttpServletResponseToList(MockHttpServletResponse response) throws JsonParseException,
      JsonMappingException, IOException {
    return convertJsonArrayStringToList(response.getContentAsString());
  }

  protected List&ltMap&ltString, Object&gt&gt convertJsonArrayStringToList(String json) throws JsonParseException, JsonMappingException, IOException {
    return mapper.readValue(json, new TypeReference&ltList&ltHashMap&ltString, Object&gt&gt&gt() {
    });
  }

  protected Map&ltString, Object&gt convertJsonHttpServletResponseToMap(MockHttpServletResponse response) throws JsonParseException,
      JsonMappingException, IOException {
    return convertJsonStringToMap(response.getContentAsString());
  }

  protected Map&ltString, Object&gt convertJsonStringToMap(String json) throws JsonParseException, JsonMappingException, IOException {
    return mapper.readValue(json, new TypeReference&ltHashMap&ltString, Object&gt&gt() {
    });
  }

  protected String convertObjectToJsonString(Object object) throws JsonMappingException, JsonGenerationException, IOException {
    return mapper.writeValueAsString(object);
  }
}
Similar to AbstractControllerTest, we can use AbstractJsonControllerTest as the parent class to all test cases involving JSON Spring Controllers.

Example

Here is how we test MyJsonController:
@RunWith(BlockJUnit4ClassRunner.class)
public class MyJsonControllerTest extends AbstractJsonControllerTest<MyJsonController> {
  public MyJsonControllerTest() {
    super(new MyJsonController());
  }

  @Test
  public void testGetUser() throws Exception {
    getRequest().setMethod("GET");
    getRequest().setServletPath("/user/jramoyo");

    ModelAndView modelAndView = invokeController();
    assertNull(modelAndView);

    String jsonString = getResponse().getContentAsString();
    assertFalse(jsonString == null || jsonString.isEmpty());

    Map<String, Object> jsonMap = convertJsonStringToMap(jsonString);
    assertEquals("jramoyo", jsonMap.get("username"));
    assertEquals("Jan", jsonMap.get("firstName"));
    assertEquals("Amoyo", jsonMap.get("lastName"));
  }
}
Line 13 asserts that ModelAndView is null as expected.

Line 16 asserts that the JSON reponse is not null nor empty.

Line 18 converts the JSON response to a Map object.

Lines 19 to 21 asserts the correctness of the user information retrieved from the HTTP response.

The above snippets are available from Google Code.

Testing Spring MVC Annotated Controllers from JUnit

Let me begin by clarifying that this post is more about testing the correctness of annotations applied to Spring Controllers rather than unit testing the behavior of the controller methods.

Annotations on Spring Controllers are commonly tested manually, together with the views. Aside from the usual concerns that come with it, manual testing also involves significant overhead with

having to redeploy binaries and restarting the web server in-between fixes. While "hot-deploy" reduces a lot of these concerns, it still leaves some room for improvement.
 
AnnotationMethodHandlerAdapter

Starting with version 2.5, Spring MVC comes with a class called AnnotationMethodHandlerAdapter which can be used to programatically invoke an annotated Spring Controller. The handle() method from this class accepts an HttpServletRequest, an HttpServletResponse, and an Object representing the Controller to invoke. It returns an instance of ModelAndView.

We can use AnnotationMethodHandlerAdapter within a JUnit test case to simulate a web container handling a request to the Spring Controller.
@Test
public void testController() throws Exception {
  ModelAndView modelAndView = handlerAdapter.handle(request, response, myController);
  assertEquals("index", modelAndView.getViewName());
}
We can mock HttpServletRequest and HttpServletResponse using popular mocking frameworks such Mockito or use the built-in MockHttpServletRequest and MockHttpServletResponse from the Spring Test module. This example uses the built-in mocked objects from Spring Test.

Using AnnotationMethodHandlerAdapter together with Generics, we can create an abstract class which serves as a parent class to all Spring Controller unit test cases:
@Ignore("abstract test case")
public abstract class AbstractControllerTest<T> {
  private final T controller;
  private final AnnotationMethodHandlerAdapter handlerAdapter;
  private MockHttpServletRequest request;
  private MockHttpServletResponse response;

  public AbstractControllerTest(T controller) {
    handlerAdapter = new AnnotationMethodHandlerAdapter();
    this.controller = controller;
  }

  @Before
  public void setUp() {
    response = new MockHttpServletResponse();
    request = new MockHttpServletRequest();
  }

  protected T getController() {
    return controller;
  }

  protected AnnotationMethodHandlerAdapter getHandlerAdapter() {
    return handlerAdapter;
  }

  protected MockHttpServletRequest getRequest() {
    return request;
  }

  protected MockHttpServletResponse getResponse() {
    return response;
  }

  protected ModelAndView invokeController() throws Exception {
    return handlerAdapter.handle(request, response, controller);
  }
}
As a parent class, it takes care of the boiler plate code involved in setting-up and invoking the controller.

Example

Let's say we have a controller which returns a view named "index" and populates the current user's username as a model attribute:
@Controller
public class MyController {
  @RequestMapping(value = "/", method = RequestMethod.GET)
  public String getHomePage(Principal principal, Model model) {
    if (principal != null) {
      model.addAttribute("username", principal.getName());
    }
    return "index";
  }
}
To test this, we create a concrete subclass of AbstractControllerTest and assign MyController as the generic type:
@RunWith(BlockJUnit4ClassRunner.class)
public class MyControllerTest 
  extends AbstractControllerTest<MyController> {

  public MyControllerTest() {
    super(new MyController());
  }

  @Test
  public void testGetHomePage() throws Exception {
    getRequest().setMethod("GET");
    getRequest().setServletPath("/");
    getRequest().setUserPrincipal(new HttpPrincipal("username", "realm"));

    ModelAndView modelAndView = invokeController();
    assertEquals("index", modelAndView.getViewName());
    assertNotNull(modelAndView.getModelMap().get("username"));
    assertEquals("username", modelAndView.getModelMap().get("username"));
  }
}
Line 6 creates an instance of MyController and passes it as a parameter to the parent class' constructor.

Lines 11 to 13 sets-up the HTTP request by assigning the request method, request URL, and a fake Principal.

Line 15 invokes the request against the Spring Controller.

Lines 16-18 asserts the correctness of the returned ModelAndView.

The above snippets are available from Google Code.

On my next post, I will explain how to test Spring Controllers returning a JSON response by extending the above classes.