Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to mock a servletContext instead of Servlet or HttpServletRequest?

I have an independent project for writing test cases; the problem is I can't mock HttpServletRequest, simply because in my servlet there are calls like getServletContext() as test cases are running from the outside servlet container. It will always return an error saying "no context found". This is just one dependency with the servlet container; there can be hundreds. For example, initialContext.lookup() also depends on a container.

How can I use Mockito to write a test case in this scenario? Please don't ask for an error message; it's more of a logical problem than technical.

Looking on the internet for tutorials makes me wonder if I am doing something seriously wrong. No one seems to have encountered this problem before... How can you mock HttpServletRequest without ever having getServletContext() called in the servlet? I mean seriously, how rare can it be?

like image 209
Vivek Avatar asked Mar 24 '14 13:03

Vivek


People also ask

What is MockHttpServletRequest?

public class MockHttpServletRequest extends Object implements HttpServletRequest. Mock implementation of the HttpServletRequest interface. The default, preferred Locale for the server mocked by this request is Locale. ENGLISH . This value can be changed via addPreferredLocale(java.

Which method is used to retrieve an attribute from a ServletContext?

Since the ServletContext object is available to all the servlets of the Web application, other servlets can retrieve the attribute from the ServletContext object by using the getAttribute() method.


1 Answers

You have a servlet implementation that uses the ServletContext, e.g.

public class SomeServlet extends HttpServlet{

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = getServletContext();
        Object attribute = servletContext.getAttribute("test");
        System.out.println(attribute.toString());
   }
}

in this case you have 2 options to test the doGet method

Use powermock's partitial mocking to only mock the getServletContext method.

@RunWith(PowerMockRunner.class)
public class SomeServletTest {

    @Test
    public void onGet() throws ServletException, IOException{
        SomeServlet someServlet = PowerMock.createPartialMock(SomeServlet.class, "getServletContext");   
        ServletContext servletContext = PowerMock.createNiceMock(ServletContext.class);
        HttpServletRequest httpServletRequest = PowerMock.createNiceMock(HttpServletRequest.class);
        HttpServletResponse httpServletResponse = PowerMock.createNiceMock(HttpServletResponse.class);

        someServlet.getServletContext();
        PowerMock.expectLastCall().andReturn(servletContext);

        servletContext.getAttribute("test");
        PowerMock.expectLastCall().andReturn("hello");

        PowerMock.replay(someServlet, servletContext, httpServletRequest, httpServletResponse);

        someServlet.doGet(httpServletRequest, httpServletResponse);
    }
}

or a simpler way is to just override the getServletContext method. In this case you don't need powermock. You can just do it using easymock. e.g.

public class SomeServletTest {

    @Test
    public void onGet() throws ServletException, IOException{
        HttpServletRequest httpServletRequest = EasyMock.createNiceMock(HttpServletRequest.class);
        HttpServletResponse httpServletResponse = EasyMock.createNiceMock(HttpServletResponse.class);
        final ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class);
        SomeServlet someServlet = new SomeServlet(){
            public ServletContext getServletContext() {
                return servletContext; // return the mock
            }
        };

        EasyMock.expect(servletContext.getAttribute("test")).andReturn("hello");
        EasyMock.replay(servletContext, httpServletRequest, httpServletResponse);

        someServlet.doGet(httpServletRequest, httpServletResponse);
    }
}

there can be 100's. like initialContext.lookup() also dependent on container.

In this case you can either create an InitialContext mock and use this. If your code creates a new InitialContext, e.g.

public void someMethod(){
    InitialContext context = new InitialContext();
    context.lookup(....);
}

you can simply extract the InitialContext instantiation to a protected method that you can override in your tests like I showed above with the ServletContext

public void someMethod(){
    InitialContext context = createInitialContext();
    context.lookup(....);
}

protected InitialContext createInitialContext(){
    return new InitialContext(); // can be overridden by a subclass 
                                 // and therefore by tests as well to
                                 // return a mock
}

If you don't want or you can't modify the code in this way, then you can use Powermock to intercept the constructor.

EDIT

Can you post your Mockito code? It would be a pleasure, since the methods are named differently.

@Test
public void onGet() throws ServletException, IOException {
  HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class);
  HttpServletResponse httpServletResponse = Mockito.mock(HttpServletResponse.class);
  final ServletContext servletContext = Mockito.mock(ServletContext.class);
  SomeServlet someServlet = new SomeServlet(){
    public ServletContext getServletContext() {
      return servletContext; // return the mock
    }
  };

  Mockito.doReturn("hello").when(servletContext).getAttribute("test");

  someServlet.doGet(httpServletRequest, httpServletResponse);
}
like image 137
René Link Avatar answered Oct 11 '22 06:10

René Link