Spring Top

Get started with Spring 5 and Spring Boot 2, through the reference Learn Spring course:

>> LEARN SPRING

1. Overview

In this quick tutorial, we'll look at a few ways to mock a HttpServletRequest object.

First, we'll start with a fully functional mock type – MockHttpServletRequest from the Spring Test library. Then, we'll see how to test using two popular mocking libraries – Mockito and JMockit. Finally, we'll see how to test using an anonymous subclass.

2. Testing HttpServletRequest

Testing Servlets can be tricky when we want to mock the client request information such as HttpServletRequest. In addition, this interface defines various methods, and there are different approaches available to mock these methods.

Let's look at the target UserServlet class that we want to test:

public class UserServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String firstName = request.getParameter("firstName");
        String lastName = request.getParameter("lastName");

        response.getWriter().append("Full Name: " + firstName + " " + lastName);
    }
}

To unit test the doGet() method, we'll need to mock both request and response parameters to simulate the actual runtime behavior.

3. Using MockHttpServletRequest from Spring

Spring-Test library provides a fully functional class MockHttpServletRequest that implements the HttpServletRequest interface.

Though this library is primarily aimed at testing Spring applications, we can use its MockHttpServletRequest class without implementing any Spring-specific functionality. In other words, even if the application doesn't use Spring we can still have this dependency just to mock HttpServletRequest objects.

Let's add this dependency to pom.xml:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.20</version>
    <scope>test</scope>
</dependency>

Now, let's see how we can use this class to test the UserServlet:

@Test
void givenHttpServletRequest_whenUsingMockHttpServletRequest_thenReturnsParameterValues() throws IOException {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("firstName", "Spring");
    request.setParameter("lastName", "Test");
    MockHttpServletResponse response = new MockHttpServletResponse();

    servlet.doGet(request, response);

    assertThat(response.getContentAsString()).isEqualTo("Full Name: Spring Test");
}

Here, we can notice that there is no actual mocking involved. We have used the fully functional request and response objects and tested the target class with just a few lines of code. As a result, the test code is clean, readable, and maintainable. 

4. Using Mocking Frameworks

Alternatively, mocking frameworks provide a clean and simple API to test mock objects that mimic the run time behavior of the original object.

Some of their strong points are their expressibility and the out-of-the-box ability to mock static and private methods. Further, we can avoid most of the boilerplate code needed for mocking (compared to custom implementations) and instead focus on the tests.

4.1. Using Mockito

Mockito is a popular open-source test automation framework that internally uses Java Reflection API to create mock objects.

Let's get started by adding the mockito-core dependency to our pom.xml:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.4.0</version>
    <scope>test</scope>
</dependency>

Next, let's see how we can mock the getParameter() method from the HttpServletRequest object:

@Test
void givenHttpServletRequest_whenMockedWithMockito_thenReturnsParameterValues() throws IOException {
    // mock HttpServletRequest & HttpServletResponse
    HttpServletRequest request = mock(HttpServletRequest.class);
    HttpServletResponse response = mock(HttpServletResponse.class);

    // mock the returned value of request.getParameterMap()
    when(request.getParameter("firstName")).thenReturn("Mockito");
    when(request.getParameter("lastName")).thenReturn("Test");
    when(response.getWriter()).thenReturn(new PrintWriter(writer));

    servlet.doGet(request, response);

    assertThat(writer.toString()).isEqualTo("Full Name: Mockito Test");
}

4.2. Using JMockit

JMockit is a mocking API that provides useful recording and verification syntax (we can use it for both JUnit and TestNG ones). It is an out-of-container integration testing library for Java EE and Spring-based apps. Let's see how we can mock HttpServletRequest using JMockit.

First, we'll add the jmockit dependency to our project:

<dependency> 
    <groupId>org.jmockit</groupId> 
    <artifactId>jmockit</artifactId> 
    <version>1.49</version>
    <scope>test</scope>
</dependency>

Next, let's proceed with the mock implementation in the test class:

@Mocked
HttpServletRequest mockRequest;
@Mocked
HttpServletResponse mockResponse;

@Test
void givenHttpServletRequest_whenMockedWithJMockit_thenReturnsParameterValues() throws IOException {
    new Expectations() {{
        mockRequest.getParameter("firstName"); result = "JMockit";
        mockRequest.getParameter("lastName"); result = "Test";
        mockResponse.getWriter(); result = new PrintWriter(writer);
    }};

    servlet.doGet(mockRequest, mockResponse);

    assertThat(writer.toString()).isEqualTo("Full Name: JMockit Test");
}

As we can see above, with just a few lines of setup, we have successfully tested the target class with a mock HttpServletRequest object.

Thus, mocking frameworks can save us a lot of legwork and make unit tests a lot faster to write. On the contrary, to use mock objects, one needs to understand the mock API, and usually, it requires a separate framework.

5. Using Anonymous Subclass

Some projects may have dependency constraints or prefer direct control over their own test class implementations. Specifically, this might be useful in the case of a larger servlet code base where the reusability of custom implementations is important. In these cases, anonymous classes come in handy.

Anonymous classes are inner classes with no name. Moreover, they are quick to implement and provide direct control over the actual object. This approach can be considered if we don't want to include an additional dependency for tests.

Now, let's create an anonymous subclass that implements the HttpServletRequest interface and use it to test the doGet() method:

public static HttpServletRequest getRequest(Map<String, String[]> params) {
    return new HttpServletRequest() {
        public Map<String, String[]> getParameterMap() {
            return params;
        }

        public String getParameter(String name) {
            String[] values = params.get(name);
            if (values == null || values.length == 0) {
                return null;
            }
            return values[0];
        }

        // More methods to implement
    }
};

Next, let's pass this request to the class under test:

@Test
void givenHttpServletRequest_whenUsingAnonymousClass_thenReturnsParameterValues() throws IOException {
    final Map<String, String[]> params = new HashMap<>();
    params.put("firstName", new String[] { "Anonymous Class" });
    params.put("lastName", new String[] { "Test" });

    servlet.doGet(getRequest(params), getResponse(writer));

    assertThat(writer.toString()).isEqualTo("Full Name: Anonymous Class Test");
}

The drawback of this solution is the need to create an anonymous class with dummy implementations for all the abstract methods. In addition, there are chances that nested objects like HttpSession may require specific implementations.

6. Conclusion

In this article, we discussed a few options for mocking the HttpServletRequest object when writing unit tests for servlets. Besides using the mocking frameworks, we saw that testing with the MockHttpServletRequest class seems to be more clean and efficient than custom implementations.

As always, the code for these examples is available over on GitHub.

Spring bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> THE COURSE
Junit footer banner
Comments are closed on this article!