Spring 通过MockMVC测试表单帖子

Spring 通过MockMVC测试表单帖子,spring,form-data,Spring,Form Data,我正在编写测试,以验证我是否可以向我们的API发布通用表单 我还添加了一些调试,但我注意到数据是通过实际表单发布的;(邮递员/AngularJS或w/e)不同于进行模拟MVC测试,如: MvcResult response = mockMvc .perform(post("/some/super/secret/url") // .param("someparam1", "somevalue") //

我正在编写测试,以验证我是否可以向我们的API发布通用表单

我还添加了一些调试,但我注意到数据是通过实际表单发布的;(邮递员/AngularJS或w/e)不同于进行模拟MVC测试,如:

MvcResult response = mockMvc
            .perform(post("/some/super/secret/url") //
                    .param("someparam1", "somevalue") //
                    .param("someparam2", "somevalue") //                
                    .contentType(MediaType.APPLICATION_FORM_URLENCODED) //
                    .accept(MediaType.APPLICATION_JSON)) //
            .andExpect(status().isOk()) //
            .andReturn();
配置与生产环境中运行的配置完全相同,诸如此类。但是,当我的拦截器记录内容时,在real-test(不是mockMVC)中,内容的格式为“someparam1=somevalue&etc=encore”

当我打印mockMVC内容时,我实际上似乎没有内容,但是请求中有参数,我假设它们是像GET参数一样添加的


有人知道如何正确地测试这个吗?我遇到这个问题是因为我们的表单帖子似乎没有被Spring解析,即使我们将FormHttpMessageConverter添加到servlet上下文中。

如果类路径上有Apache HTTPComponents HttpClient,您可以这样做:

    mockMvc.perform(post("/some/super/secret/url")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .content(EntityUtils.toString(new UrlEncodedFormEntity(Arrays.asList(
                    new BasicNameValuePair("someparam1", "true"),
                    new BasicNameValuePair("someparam2", "test")
            )))));
如果您没有HttpClient,则可以使用一个简单的帮助器方法来构造urlencoded表单实体:

    mockMvc.perform(post("/some/super/secret/url")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .content(buildUrlEncodedFormEntity(
         "someparam1", "value1", 
         "someparam2", "value2"
    ))));
使用此辅助功能:

private String buildUrlEncodedFormEntity(String... params) {
    if( (params.length % 2) > 0 ) {
       throw new IllegalArgumentException("Need to give an even number of parameters");
    }
    StringBuilder result = new StringBuilder();
    for (int i=0; i<params.length; i+=2) {
        if( i > 0 ) {
            result.append('&');
        }
        try {
            result.
            append(URLEncoder.encode(params[i], StandardCharsets.UTF_8.name())).
            append('=').
            append(URLEncoder.encode(params[i+1], StandardCharsets.UTF_8.name()));
        }
        catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
    return result.toString();
 }
私有字符串buildUrlEncodedFormEntity(字符串…参数){
如果((参数长度%2)>0){
抛出新的IllegalArgumentException(“需要给出偶数个参数”);
}
StringBuilder结果=新建StringBuilder();
对于(int i=0;i 0){
result.append('&');
}
试一试{
结果。
append(URLEncoder.encode(params[i],StandardCharsets.UTF_8.name())。
追加('=')。
append(URLEncoder.encode(params[i+1],StandardCharsets.UTF_8.name());
}
捕获(不支持的编码异常e){
抛出新的运行时异常(e);
}
}
返回result.toString();
}

您也可以使用我创建的这个小库:

在pom.xml中添加依赖项:

<dependency>
    <groupId>io.florianlopes</groupId>
    <artifactId>spring-mvc-test-utils</artifactId>
    <version>1.0.1</version>
    <scope>test</scope>
</dependency>
这个库只是根据表单对象将参数添加到MockMvc请求中


以下是我编写的详细教程:

以下是Kotlin SpringBoot示例:

@RunWith(MockitoJUnitRunner::class)
类ApiFormControllerTest{
lateinit var mvc:MockMvc
@注射模拟
lateinit变量apiFormController:apiFormController
@以前
趣味设置(){
mvc=MockMvcBuilders.standaloneSetup(apiFormController.setControllerAdvice(ExceptionAdvice()).build()
}
有趣的MockHttpServletRequestBuilder.withForm(参数:Map):MockHttpServletRequestBuilder{
this.contentType(MediaType.APPLICATION\u FORM\u URLENCODED)
.内容(
EntityUtils.toString(
UrlEncodedFormEntity(
params.entries.toList().map{BasicNameValuePair(it.key,it.value)}
)
)
)
还这个
}
@试验
fun canSubmitValidForm(){
mvc.perform(post(“/forms”).withForm(mapOf(“subject”)到“hello”))
.andExpect(状态().isOk)
}
}

您能否提供更多详细信息,如配置、发布到服务器的真实数据以及一些日志详细信息,以使其更加清晰?关于mock,看起来你做得不错,mock测试的参数与你显示的post数据的格式相同。此外,请检查您是否有类似spring security的想法。在执行下一步之前,您可能需要模拟spring安全性或执行登录。我们没有使用类似的方法,但我发现我的示例实际上是错误的。在本例中,someParam1和someParam2将是一个查询参数(在url中)。我想使用params形式。然而,这似乎只能通过模型或值映射进行访问。所以我们需要一个解决方案(这是迁移到完全spring mvc的,以前是@FormParam,不是spring)。哦,发布的数据,就像我在mvc部分下面的示例中,它发布了一个包含&and=的内容字符串。由于缺乏细节,我不知道如何在控制器中处理post数据。但是基于应用程序表单url编码,您发布的参数是正确的格式。这里的参数并不意味着只有在url中,它也可以在post内容中使用相同的格式
&name=value
,同样的设置在Spring Boot 2.2.6上对我来说很好。所以,我想他们已经解决了这个问题。这不是最好的,但这是一个解决方案。您能告诉我为什么需要Apache HTTPComponents吗?据我所知,这是可以实现的没有任何额外的图书馆。现在,作为唯一的答案,我会接受这一点,似乎很难在MockMVCW中测试为什么你认为我的解决方案不好?你不需要HTTPComponents,我只是使用它,因为我在类路径上有它。您可以使用任何能够构造URL编码表单实体的库,该实体只不过是
urlencode('name1')+'='+urlencode(value1)+'&+…
。您可以编写自己的helper方法来构造itI,我想我的意思是,如果在mockMVC中有一个实现,它会更好,这很好,但它仍然不是一个解决方法。有趣的是,在我有一段时间没有使用Java之后,我回答了你可能想读的内容
mockMvc.perform(MockMvcRequestBuilderUtils.postForm("/users", new AddUserForm("John", "Doe", null, new Address(1, "Street", 5222, "New York"))))
    .andExpect(MockMvcResultMatchers.status().isFound())
    .andExpect(MockMvcResultMatchers.redirectedUrl("/users"))
    .andExpect(MockMvcResultMatchers.flash().attribute("message", "success"));