Hot questions for Using Mockito in servlets

Question:

I am trying to perform some jUnit test in one of my servlets but whenever I try to run it I obtain a ExceptionInInitializerError. I've read that this error is due to an exception, occurred during evaluation of a static initializer or the initializer for a static variable. The thing is that even though I tried to fix it I can. That's why I am writing here: my servlet code is the following:

public class AppServlet extends HttpServlet {

// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
/**
 * Handles the HTTP <code>GET</code> method.
 *
 * @param request servlet request
 * @param response servlet response
 * @throws ServletException if a servlet-specific error occurs
 * @throws IOException if an I/O error occurs
 */
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        //obtenemos los valores de los campos del formulario.
        String usr = request.getParameter("usrName");
        String cp = request.getParameter("codigoPostal");
        Gson gson = new Gson();
        if (usr == null || cp == null || cp.length() != 5) {
            Result r = new Result("KO", "No se introdujeron bien los datos");
            String jsonString = gson.toJson(r);
            out.println(jsonString);
            return;
        }

        //procedemos a convertir el codigo postal en la ciudad usando geonames:
        //para ello usaremos la api de geonames
        String city = geoLocalize.localizeCity(cp);

        //empezaremos con el codigo de depuración para ver donde podemos tener errores
        if (city == null) {
            Result r = new Result("KO", "No hay ciudad para dicho codigo postal");
            String jsonString = gson.toJson(r);
            out.println(jsonString);
            return;
        }
        //comenzamos con las bases de datos
        SQLconnection db = new SQLconnection();
        //una vez creada la conexion deberemos hacer las insert en las tablas.

        if (!db.checkUsr(usr)) {
            if (db.insertUsr(usr)) {
                int numCp = parseInt(cp);
                if (!db.checkCP(numCp)) {
                    if (db.addCity(numCp, city)) {
                        Result r = new Result("OK", "Proceso terminado sin problemas");
                        String jsonString = gson.toJson(r);
                        out.println(jsonString);
                        return;
                    } else {
                        Result r = new Result("KO", "No se ha podido añadir la ciudad");
                        String jsonString = gson.toJson(r);
                        out.println(jsonString);
                        return;
                    }

                } else {
                    Result r = new Result("OK", "Se ha añadido el usuario, el codigo postal ya estaba");
                    String jsonString = gson.toJson(r);
                    out.println(jsonString);
                    return;
                }
            } else {
                Result r = new Result("KO", "No se ha podido añadir el usuario");
                String jsonString = gson.toJson(r);
                out.println(jsonString);
                return;
            }

        } else {
            Result r = new Result("KO", "El usuario ya existe en el sistema");
            String jsonString = gson.toJson(r);
            out.println(jsonString);
            return;
        }

    } catch (IOException | NumberFormatException ex) {
        Logger.getLogger(AppServlet.class.getName()).log(Level.SEVERE, null, ex);
    } catch (Exception ex) {
        Logger.getLogger(AppServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
}

}

And my jUnit test code is the following:

public class AppServletTest extends TestCase {

HttpServletRequest request;
HttpServletResponse response;
AppServlet instance;

public AppServletTest(String testName) {
    super(testName);

}


@Override
protected void setUp() throws Exception {
    request = mock(HttpServletRequest.class);
    response = mock(HttpServletResponse.class);
    instance = new AppServlet();
    super.setUp();
}


@Override
protected void tearDown() throws Exception {
    super.tearDown();
}

/**
 * Test of doGet method, of class AppServlet.
 *
 * @throws java.lang.Exception
 */

public void testDoGet() throws Exception {
    System.out.println("doGet");

    //generamos los parametros y un .txt donde guardaremos la respuesta JSON
    when(request.getParameter("usrName")).thenReturn("Javi");
    when(request.getParameter("codigoPostal")).thenReturn("48991");
    PrintWriter writer = new PrintWriter("resultadoPruebas.txt");
    when(response.getWriter()).thenReturn(writer);

    //mandamos la peticion al servlet
    instance.doGet(request, response);

    verify(request, atLeast(1)).getParameter("usrName"); // para verificar si se ha llamado a usrName
    writer.flush(); // it may not have been flushed yet...
    assertTrue(FileUtils.fileRead(new File("somefile.txt"), "UTF-8")
            .contains("OK"));


}

}

An here is the full stacktrace:

java.lang.ExceptionInInitializerError
at com.jbo.testapp.AppServletTest.setUp(AppServletTest.java:36)
at junit.framework.TestCase.runBare(TestCase.java:128)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:120)
at junit.framework.TestSuite.runTest(TestSuite.java:230)
at junit.framework.TestSuite.run(TestSuite.java:225)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:96)
at org.apache.maven.surefire.junit.JUnit3Provider.executeTestSet(JUnit3Provider.java:117)
at org.apache.maven.surefire.junit.JUnit3Provider.invoke(JUnit3Provider.java:94)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)
at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)
at 

org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)
Caused by: java.util.MissingResourceException: Can't find bundle for base name javax.servlet.LocalStrings, locale es_ES
    at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:1564)
    at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1387)
    at java.util.ResourceBundle.getBundle(ResourceBundle.java:773)
    at javax.servlet.GenericServlet.<clinit>(GenericServlet.java:95)
    ... 24 more
Caused by: java.util.MissingResourceException: Can't find bundle for base name javax.servlet.LocalStrings, locale es_ES
    at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:1564)
    at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1387)
    at java.util.ResourceBundle.getBundle(ResourceBundle.java:773)
    at javax.servlet.GenericServlet.<clinit>(GenericServlet.java:95)
    ... 24 more

Hope you guys can help me! Thank you in advance


Answer:

Caused by: java.util.MissingResourceException: Can't find bundle for base name javax.servlet.LocalStrings, locale es_ES

That's the real error.

Your running tests are missing the servlet-api dependency.

If you're using maven make sure this dependency is in your project:

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
</dependency>

Question:

I've made a basic test for servlet to test it's response status code, but it doesn't work - it's always 0, although I've set the response status code inside the servlet to 200.

public class TestMyServlet extends Mockito {

@Test
public void test() throws Exception {
    HttpServletRequest request = mock(HttpServletRequest.class);
    HttpServletResponse response = mock(HttpServletResponse.class);

    when(request.getParameter("test")).thenReturn("1");

    new MyServlet().doPost(request, response);

    System.out.println(response.isCommited()); // false
    System.out.println(response.getContentType()); // null
    System.out.println(response.getStatus()); // 0
  }
}

How to let this work?


Answer:

You want to test this differently. You need to verify that your inputs caused the expected outputs. For non-mock results, you would assert the behavior. Since you want to verify that your outputs were set properly.

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class MyServletTests {
    @Test
    public void testValidRequest() throws Exception {
        HttpServletRequest request = mock(HttpServletRequest.class);
        HttpServletResponse response = mock(HttpServletResponse.class);

        when(request.getParameter("test")).thenReturn("1");

        new MyServlet().doPost(request, response);

        // ensure that the request was used as expected
        verify(request).getParameter("test");

        // ensure that the response was setup as expected based on the
        //  mocked inputs
        verify(response).setContentType("text/html");
        verify(response).setStatus(200);
    }
 }

If you expect something to not be touched given certain inputs, then you should consider verifying that behavior using verify(response, never()).shouldNotBeCalledButSometimesIs() (to verify when conditions control it being called/set versus not).

Question:

How to mock HttpServletRequest getResourceAsStream in a Java unit test? I am using it to read a resource file from a servlet request.

HttpServletRequest.getSession().getServletContext().getResourceAsStream()

I am using org.mockito.Mock to Mock HttpServletRequest.


Answer:

There is quite a bit of mocking you need to do. I would suggest using annotations:

import static org.mockito.Mockito.when;

public class TestClass{

   @Mock
   private HttpServletRequest httpServletRequestMock;

   @Mock
   private HttpSession httpsSessionMock;

   @Mock
   private ServletContext servletContextMock;

   @Before
   public void init(){
      MockitoAnnotations.initMocks(this);
   }

   @Test
   public void test(){
       // Arrange
       when(httpServletRequestMock.getSession()).thenReturn(httpSessionMock);
       when(httpSessionMock.getServletContext()).thenReturn(servletContextMock);

       InputStream inputStream = // instantiate;

       when(servletContextMock.getResourceAsStream()).thenReturn(inputStream);

       // Act - invoke method under test with mocked HttpServletRequest

   }
}

Question:

I have this filter class and need to have code coverage as high as possible while testing with junit.

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    String userId= request.getHeader(Constants.USER_ID);

    if (StringUtils.isEmpty(userId)) {
        LOGGER.error("User Id is missing in request header.");
        isAuthorized = false;
    } 

    if (!isAuthorized) {
        LOGGER.warn("Authorization failed: User ID =[{}] is not authorized to access.", userId);
        response.setContentType("text/html; charset=UTF-8");
        response.getWriter().write(errorMsg);
    } else {
        filterChain.doFilter(request, response);
    }
}

And test class:

@RunWith(MockitoJUnitRunner.class)
public class SampleFilterTest {

    @Mock
    FilterConfig filterConfig;

    ServletRequest servletRequest;
    ServletResponse servletResponse;
    HttpServletRequest request;

    @Mock
    FilterChain filterChain;

    @Mock
    HttpServletResponse httpServletResponse;


    @InjectMocks
    SampleFilter sampleFilter;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void init() throws ServletException {
        sampleFilter.init(filterConfig);
    }

    @Test
    public void doFilter() throws IOException, ServletException{
        //when(request.getHeader(Constants.USER_ID)).thenReturn("batman");
        sampleFilter.doFilter(servletRequest, servletResponse, filterChain);
    }

    @Test
    public void destroy() {
        sampleFilter.destroy();
    }
}

When I run doFilter() it returns NullPointerException at

String userId= request.getHeader(Constants.USER_ID);

line.

How do I avoid this?

I need to call this method and execute whatever is inside to provide required code coverage.


Answer:

The problem is that

HttpServletRequest request = (HttpServletRequest) servletRequest;

is casting to null because you pass in a ServletRequest which wont cast to HttpServletRequest

Mock the actual type you are trying to casting it to within the method under test.

For example

//...

@Test
public void doFilter() throws IOException, ServletException {
    //Arrange
    //mock HttpServletResponse so cast does not fail
    ServletRequest request = mock(HttpServletRequest.class);
    when(request.getHeader(Constants.USER_ID)).thenReturn("batman");

    ServletResponse response = mock(HttpServletResponse.class);
    //setup response as neded. Looks like `.getWriter().write(errorMsg);` needs mocking

    //Act
    sampleFilter.doFilter(request, response, filterChain);

    //Assert
    //...
}

Question:

Class Permission implements ContainerRequestContext
{

    @context
    HttpServletRequest servletRequest;

    public void filter(ContainerRequestContext containerRequestContext) throws IOException 
    {
        String Path=servletRequest.getPathInfo();

        int id=containerRequestContext.getId();

        if(id==null){
            containerRequestContext.abortWith(new ServerResponse(ExceptionUtil.getErrorMessageObject(...),errorCode,new Header<Object>()));
        }
    }
}

How to write the Test for the filter method. I am able to mock the ContainerReqeustContext. But how to mock the HttpServletRequest since it is not the input of the filter() method.


Answer:

You can use @InjectMocks annotation:

@RunWith(MockitoJUnitRunner.class)
class Test {

   @InjectMocks
   private Permission permission;
   @Mock
   private HttpServletRequest servletRequest;

   @Test
   public void test()
   {
       permission.filter(...);
   }
}

Or you can use a simply setter.

Class Permission implements ContainerRequestContext
{
    @context
    HttpServletRequest servletRequest;

    public void filter(ContainerRequestContext containerRequestContext) throws IOException 
    {
          ...
    }

   void setHttpServletRequest(HttpServletRequest servletRequest) {
     this.servletRequest = servletRequest;
   {
}

 @RunWith(MockitoJUnitRunner.class)
 class Test {

     private Permission permission = new Permission();
     @Mock
     private HttpServletRequest servletRequest;

     @Test
     public void test()
     {
        permission.setHttpServletRequest(servletRequest);
        permission.filter(...);
     }
  }

Question:

I have a test for simple Servlet which get jsp file. In this servlet action request.setAttribute("key", "value") but after call .forward() when I do request.getAttribute("key") I'm get null. Why this happen? This behavior determines forward or reason in mock object?

This is doPost of Servlet:

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

    final boolean success = addUserInDatabase(req);

    if (success) req.setAttribute("serverAnswer", EDIT_SUCCESS.get());//this write value
    else req.setAttribute("serverAnswer", ERR_UNIQUE_L_P.get());

    req.getRequestDispatcher(ANSWER.get())
            .forward(req, resp);
}

This is test:

//mock http.
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);


RequestDispatcher dispatcher = mock(RequestDispatcher.class);
when(request.getRequestDispatcher(ANSWER.get()))
                  .thenReturn(dispatcher);
servlet.doPost(request, response);
String result = (String) request.getAttribute("serverAnswer");//get value
Assert.assertThat(result, is(EDIT_SUCCESS.get()));// result == null

Why I get Null? Is it possible to get value of setAttribute after call forward? How to test this behavior? Thank You.


Answer:

If you add String result = (String)req.getAttribute("serverAnswer"); just before calling req.getRequestDispatcher(ANSWER.get()) on your servlet and check value of result it would still be null.

The reason is your request object is not real but mocked. you have to do something like this.

when(request.getAttribute(eq("serverAnswer"))).thenReturn(EDIT_SUCCESS.get());

Question:

I have a filter that looks something like this:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    // Get the servlet request
    HttpServletRequest req = (HttpServletRequest) request;

    ... Some logic code here ...

    if (!accepted.contains(req.getRequestURI())) {
        HttpServletResponse res = (HttpServletResponse) response;
        res.sendError(404, "Some Error");
        return;
    }

    // Pass the request along the filter chain
    chain.doFilter(request, response);
}

The problem is with my test case. Normally I would use mockito to mock the HttpServletRequest something like this:

HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRequestURI()).thenReturn("the test URI");

However, the fact that the HttpServletRequest is obtained inside the method by casting I'm not sure the best way to test various URI requests.


Answer:

You should be still be able to call your servlet with doFilter like:

HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRequestURI()).thenReturn("the test URI");
HttpServletResponse response = mock(HttpServletResponse.class);
//mock method calls on response, replay
myServlet.filter(request, response);

It's fine even if it internally does type casting.