Friday, 5 April 2013

Partially mocking a class, mixing mock and real implementation

In this particular case, I tried to test a servlet but I needed mock an inherited internal method getServletContext() , Thus spy(T) function creates a partial mock object, mixing mock and real functionality in the same object, this is an example how I used it.

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.nebulaobjects.services.ForwardServlet;
import com.nebulaobjects.services.TestBase;

public class TestForwardServlet extends TestBase {

 protected ForwardServlet real, spy;
 protected ServletContext ctx;
 protected RequestDispatcher dispatcher;
 protected HttpServletRequest request;
 protected HttpServletResponse response;
 protected ServletConfig conf;

 public void setUp() {
  real = new ForwardServlet();

  spy = spy(real);
  conf = mock(ServletConfig.class);
  ctx = mock(ServletContext.class);
  request = mock(HttpServletRequest.class);
  response = mock(HttpServletResponse.class);
  dispatcher = mock(RequestDispatcher.class);

  when(ctx.getRequestDispatcher(anyString())).thenReturn(dispatcher);
  doReturn(ctx).when(spy).getServletContext();

 }

 public void testForward() throws ServletException, IOException {
  String version = "v1";

  doReturn(version).when(spy).getInitParameter("version");
  spy.init(conf);

  when(request.getRequestURI()).thenReturn(
    "/services/health/1/3/4/5/6/7/8/9/10/service");

  spy.forward(request, response);
  verify(ctx, times(1)).getRequestDispatcher(
    "/services/" + version
      + "/health/1/3/4/5/6/7/8/9/10/service");
 }
}