如何从URL字符串创建一个模拟的HttpServletRequest?

84
我有一个服务,需要对HttpServletRequest对象进行一些操作,具体是使用request.getParameterMap和request.getParameter构造一个对象。
我想知道是否有一种简单的方法来获取给定的URL,以字符串形式提供,例如:
String url = "http://www.example.com/?param1=value1&param";

我该如何将其轻松转换为HttpServletRequest对象,以便可以使用单元测试进行测试?或者至少让request.getParameterMap和request.getParameter正确工作?

5个回答

66

这里是如何使用MockHttpServletRequest的方法:

// given
MockHttpServletRequest request = new MockHttpServletRequest();
request.setServerName("www.example.com");
request.setRequestURI("/foo");
request.setQueryString("param1=value1&param");

// when
String url = request.getRequestURL() + '?' + request.getQueryString(); // assuming there is always queryString.

// then
assertThat(url, is("http://www.example.com:80/foo?param1=value1&param"));

1
使用MockHttpServletRequest实际上可以胜任工作,因为我只需要一个完全功能的HttpServletRequest。 - Duc Tran
3
请注意,在设置queryString时,MockHttpServletRequest中的参数映射不会自动更新。因此,如果您在代码中使用request.getParameter(),即使这一步看起来有些多余,您仍需要在测试准备阶段明确设置它。 - Nicola Ambrosetti
我也注意到了这个问题。查询字符串和参数似乎没有同步。 - Raj
这需要 JEE API Jar 6 或 8 版本。 - Smart Coder
过时的 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/mock/web/MockHttpServletRequest.html - Simplicity's_Strength

53

Spring框架在其spring-test模块中提供了MockHttpServletRequest

如果您使用的是Maven,可能需要在您的pom.xml文件中添加相应的依赖。您可以在mvnrepository.com上找到spring-test。


40

最简单的模拟HttpServletRequest的方法:

  1. 创建一个匿名子类:

HttpServletRequest mock = new HttpServletRequest ()
{
    private final Map<String, String[]> params = /* whatever */

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

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

    // TODO *many* methods to implement here
};
  • 使用 jMockMockito 或其他通用的模拟框架:

  • HttpServletRequest mock = context.mock(HttpServletRequest.class); // jMock
    HttpServletRequest mock2 = Mockito.mock(HttpServletRequest.class); // Mockito
    
  • 使用HttpUnit的 ServletUnit,不要模拟请求。


  • 在注释中的 WHATEVER 是什么? - ruby_object
    @ruby_object 是一个参数映射的 Map,在单元测试中用于解析。当我们传递 URL/?search=xyz 时,这个 Map 将包含 search 映射到 xyz 的内容。 - ankush__
    方案1行不通,因为无法构造HttpServletRequest - basZero

    4
    您通常会在集成测试中测试这些类型的内容,该测试实际上连接到服务。要进行单元测试,您应该测试servlet的doGet / doPost方法使用的对象。
    通常情况下,您不希望在servlet方法中有太多的代码,您需要创建一个bean类来处理操作,并将自己的对象传递给它,而不是servlet API对象。

    1
    再说,你为什么要因为某个方法需要特定类型的参数作为输入而放弃增加测试覆盖率(即代码的健壮性)的机会呢? - pap
    1
    这要看你是想对Servlet API进行单元测试还是想对自己的代码进行单元测试? - Rocky Pulley
    5
    通常情况下,你不希望在servlet方法中有任何代码。但实际上,你总是会有一些帮助类、工具方法、解析、验证、模型转换,甚至有时还会有业务逻辑(真是可怕!)。因为这些存在,你会想要对它们进行单元测试。而为了做到这一点,你需要模拟servlet请求以创建明确定义的测试场景。模拟servlet API恰恰与测试相反,它完全从测试用例中移除了它。 - pap

    2

    对于那些想要模拟带有Json负载的POST HttpServletRequest的人来说,以下是Kotlin示例,但这里的关键是使用DelegatingServetInputStream,当您想要模拟HttpServletRequest中的request.getInputStream时使用。

    @Mock
    private lateinit var request: HttpServletRequest
    
    @Mock
    private lateinit var response: HttpServletResponse
    
    @Mock
    private lateinit var chain: FilterChain
    
    @InjectMocks
    private lateinit var filter: ValidationFilter
    
    
    @Test
    fun `continue filter chain with valid json payload`() {
        val payload = """{
          "firstName":"aB",
          "middleName":"asdadsa",
          "lastName":"asdsada",
          "dob":null,
          "gender":"male"
        }""".trimMargin()
    
        whenever(request.requestURL).
            thenReturn(StringBuffer("/profile/personal-details"))
        whenever(request.method).
            thenReturn("PUT")
        whenever(request.inputStream).
            thenReturn(DelegatingServletInputStream(ByteArrayInputStream(payload.toByteArray())))
    
        filter.doFilter(request, response, chain)
    
        verify(chain).doFilter(request, response)
    }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接