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.