简体   繁体   中英

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?

You have a servlet implementation that uses the ServletContext , eg

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. eg

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 , eg

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM