用 url 字符串创建一个模拟 HttpServletRequest?

Posted

技术标签:

【中文标题】用 url 字符串创建一个模拟 HttpServletRequest?【英文标题】:Creating a mock HttpServletRequest out of a url string? 【发布时间】:2011-09-21 06:32:19 【问题描述】:

我有一个服务对 HttpServletRequest 对象做一些工作,特别是使用 request.getParameterMap 和 request.getParameter 来构造一个对象。

我想知道是否有一种直接的方法来获取提供的 url,以字符串的形式,比如

String url = "http://www.example.com/?param1=value1&param";

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

【问题讨论】:

【参考方案1】:

这里是如何使用 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"));

【讨论】:

使用这个 MockHttpServletRequest 实际上可以完成这项工作,因为我只需要一个功能齐全的 HttpServletRequest 请注意,在设置 queryString 时,参数 Map 不会在 MockHttpServletRequest 中自动更新。因此,如果您在代码中使用 request.getParameter(),您必须首先明确设置它作为测试设置,即使它一开始可能看起来是多余的。 我也注意到了。查询字符串和参数似乎没有同步 jee api jar 6 或 8 为此将需要。 过时的docs.spring.io/spring-framework/docs/current/javadoc-api/org/…【参考方案2】:

Spring 在其 spring-test 模块中有MockHttpServletRequest。

如果您使用的是 maven,您可能需要将适当的依赖项添加到您的 pom.xml。您可以在 mvnrepository.com 找到 spring-test。

【讨论】:

【参考方案3】:

模拟HttpServletRequest 的最简单方法:

    创建一个匿名子类:

    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
    ;
    

    使用jMock、Mockito 或其他一些通用模拟框架:

    HttpServletRequest mock = context.mock(HttpServletRequest.class); // jMock
    HttpServletRequest mock2 = Mockito.mock(HttpServletRequest.class); // Mockito
    

    使用 HttpUnit 的 ServletUnit 并且根本不要模拟请求。

【讨论】:

cmets 中的 WHATEVER 是什么? @ruby_object 无论是要在单元测试中解析的参数映射。然后,当我们传递 URL/?search=xyz 时,这将包含搜索 xyz 的映射。【参考方案4】:

您通常会在实际上连接到服务的集成测试中测试这类事情。要进行单元测试,您应该测试 servlet 的 doGet/doPost 方法使用的对象。

一般来说,您不希望 servlet 方法中有太多代码,您可能希望创建一个 bean 类来处理操作并将您自己的对象传递给它,而不是 servlet API 对象。

【讨论】:

再一次,为什么你会因为方法恰好将某种类型的参数作为输入而放弃增加测试覆盖率(即代码稳健性)的机会? 这取决于,您是要对 servlet API 进行单元测试,还是要对自己的代码进行单元测试? “一般来说”你不想在你的 servlet 方法中有任何代码。在实践中,你总是会的。您将拥有辅助类、实用方法、解析、验证、模型翻译,有时甚至是业务逻辑(太可怕了!)。既然你这样做了,你就会想要对它们进行单元测试。因此,您需要模拟 servlet 请求以创建定义良好的测试场景。模拟 servlet API 恰恰是测试它的对立面。它将完全从测试用例中删除。【参考方案5】:

对于那些寻找使用 Json 有效负载模拟 POST HttpServletRequest 的方法的人来说,下面是在 Kotlin 中,但是当你想模拟 @ 时,这里的关键是 DelegatingServetInputStream 987654323@来自HttpServletRequest

@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)

【讨论】:

以上是关于用 url 字符串创建一个模拟 HttpServletRequest?的主要内容,如果未能解决你的问题,请参考以下文章

URL 验证发布者

jsp页面提示“Multiple annotations found at this line: - The superclass "javax.servlet.http.HttpServl

用 Behat 模拟添加元素奏鸣曲

CCF(URL映射:80分):字符串处理+模拟

C#用httpclient模拟post到web网站上

HttpClient 模拟登陆知乎