How to Mock HttpServletRequest – 如何模拟HttpServletRequest

最后修改: 2022年 6月 4日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

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

在这个快速教程中,我们将看一下模拟HttpServletRequest对象的几种方法

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.

首先,我们将从一个功能齐全的模拟类型开始–来自Spring测试库的MockHttpServletRequest。然后,我们将看到如何使用两个流行的模拟库–Mockito和JMockit进行测试。最后,我们将看到如何使用一个匿名子类进行测试。

2. Testing HttpServletRequest

2.测试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.

测试Servlet可能很棘手,当我们想要模拟客户端请求信息时,例如HttpServletRequest。此外,这个接口定义了各种方法,有不同的方法可以用来模拟这些方法。

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

让我们看看我们要测试的目标UserServlet类。

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.

为了对doGet()方法进行单元测试,我们需要模拟requestresponse参数以模拟实际的运行时行为。

3. Using MockHttpServletRequest from Spring

3.使用来自Spring的MockHttpServletRequest

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

Spring-Test库提供了一个全功能的类MockHttpServletRequest,它实现了HttpServletRequest接口。

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.

尽管这个库主要是针对测试Spring应用程序的,我们可以使用它的MockHttpServletRequest类,而无需实现任何Spring特定的功能。换句话说,即使应用程序没有使用Spring,我们仍然可以通过这个依赖关系来模拟HttpServletRequest对象

Let’s add this dependency to pom.xml:

让我们把这个依赖性添加到 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:

现在,让我们看看如何使用这个类来测试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

4.使用嘲讽框架

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

另外,嘲讽框架提供了一个干净而简单的API来测试模仿原始对象运行时行为的嘲讽对象

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.

它们的一些强项是它们的可表达性和开箱即用的模拟staticprivate方法的能力。此外,我们可以避免嘲讽所需的大部分模板代码(与自定义实现相比),而将重点放在测试上。

4.1. Using Mockito

4.1.使用Mockito

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

Mockito是一个流行的开源测试自动化框架,其内部使用Java Reflection API来创建模拟对象。

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

让我们开始吧,将mockito-core 依赖性添加到我们的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:

接下来,让我们看看如何从HttpServletRequest对象中模拟getParameter()方法。

@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

4.2.使用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.

JMockit是一个嘲弄API,它提供了有用的记录和验证语法(我们可以将其用于JUnitTestNG的)。它是一个用于Java EE和基于Spring的应用程序的容器外集成测试库。让我们看看我们如何使用JMockit来模拟HttpServletRequest

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

首先,我们将把jmockit 依赖添加到我们的项目中。

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

正如我们在上面看到的,只需几行设置,我们就成功地用一个模拟的HttpServletRequest对象测试了目标类。

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.

因此,模拟框架可以为我们节省大量的腿部工作,使单元测试的编写速度大大加快。相反,要使用mock对象,需要了解mock API,而且通常需要一个单独的框架。

5. Using Anonymous Subclass

5.使用匿名子类

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.

有些项目可能有依赖性限制,或者喜欢直接控制自己的测试类实现。具体来说,在一个较大的servlet代码库中,自定义实现的可重用性很重要,这可能很有用。在这种情况下,匿名类就会派上用场。

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:

现在,让我们创建一个匿名子类,实现HttpServletRequest接口,并使用它来测试doGet()方法。

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.

这种解决方案的缺点是需要创建一个匿名类,为所有的抽象方法提供假的实现。此外,HttpSession这样的嵌套对象有可能需要特定的实现

6. Conclusion

6.结语

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.

在这篇文章中,我们讨论了在为servlets编写单元测试时对HttpServletRequest对象进行嘲弄的几个选项。除了使用嘲弄框架外,我们看到使用MockHttpServletRequest类进行测试似乎比自定义实现更干净、更高效。

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

像往常一样,这些例子的代码可以在GitHub上找到over