通过 MockMVC 测试表单发布
Testing Form posts through MockMVC
我正在编写测试来验证我是否可以对我们的 API 做一个通用形式 post。
我还添加了一些调试,但我注意到数据 post 是由实际表格编辑的; (Postman / AngularJS or w/e) 不同于像这样的 mockMVC 测试:
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();
配置与生产中的配置 运行 完全相同,等等。但是,当我的拦截器记录内容时,在实际测试(不是 mockMVC)中,内容的格式类似于 "someparam1=somevalue&etc=encore"
当我打印 mockMVC 内容时,我实际上似乎没有内容,但是请求中有 Params,我假设它们是像 GET 参数一样添加的。
有人知道如何正确测试吗?我遇到了这个问题,因为看起来我们的表单 posts 似乎没有被 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();
}
您也可以使用我创建的这个小型库:https://github.com/f-lopes/spring-mvc-test-utils/。
在pom.xml中添加依赖:
<dependency>
<groupId>io.florianlopes</groupId>
<artifactId>spring-mvc-test-utils</artifactId>
<version>1.0.1</version>
<scope>test</scope>
</dependency>
与 MockMvc 一起使用:
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"));
这个库只是根据表单对象将参数添加到 MockMvc 请求中。
下面是我写的详细教程:https://blog.florianlopes.io/tool-for-spring-mockmvcrequestbuilder-forms-tests/
这是一个 Kotlin SpringBoot 示例:
@RunWith(MockitoJUnitRunner::class)
class ApiFormControllerTest {
lateinit var mvc: MockMvc
@InjectMocks
lateinit var apiFormController: ApiFormController
@Before
fun setup() {
mvc = MockMvcBuilders.standaloneSetup(apiFormController).setControllerAdvice(ExceptionAdvice()).build()
}
fun MockHttpServletRequestBuilder.withForm(params: Map<String, String>): MockHttpServletRequestBuilder {
this.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.content(
EntityUtils.toString(
UrlEncodedFormEntity(
params.entries.toList().map { BasicNameValuePair(it.key, it.value) }
)
)
)
return this
}
@Test
fun canSubmitValidForm() {
mvc.perform(post("/forms").withForm(mapOf("subject" to "hello")))
.andExpect(status().isOk)
}
}
现代 spring (5.3.12) 提供的解决方案无效。使用 MockHttpServletRequestBuilder 中的 param 方法似乎有一个简单而优雅的解决方案:
mockMvc.perform(post("/some/super/secret/url")
.param("someparam1", true)
.param("someparam2", false)
.with(csrf())
).andExpect(status().isOk)
注意:因为我正在使用 Spring 安全性,所以我需要添加
.with(csrf())
以便 CSRF 后处理器允许我的请求。如果不是,Spring 安全将拒绝请求以避免 跨站点请求伪造 (CSRF) 攻击。
我正在编写测试来验证我是否可以对我们的 API 做一个通用形式 post。
我还添加了一些调试,但我注意到数据 post 是由实际表格编辑的; (Postman / AngularJS or w/e) 不同于像这样的 mockMVC 测试:
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();
配置与生产中的配置 运行 完全相同,等等。但是,当我的拦截器记录内容时,在实际测试(不是 mockMVC)中,内容的格式类似于 "someparam1=somevalue&etc=encore"
当我打印 mockMVC 内容时,我实际上似乎没有内容,但是请求中有 Params,我假设它们是像 GET 参数一样添加的。
有人知道如何正确测试吗?我遇到了这个问题,因为看起来我们的表单 posts 似乎没有被 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();
}
您也可以使用我创建的这个小型库:https://github.com/f-lopes/spring-mvc-test-utils/。
在pom.xml中添加依赖:
<dependency>
<groupId>io.florianlopes</groupId>
<artifactId>spring-mvc-test-utils</artifactId>
<version>1.0.1</version>
<scope>test</scope>
</dependency>
与 MockMvc 一起使用:
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"));
这个库只是根据表单对象将参数添加到 MockMvc 请求中。
下面是我写的详细教程:https://blog.florianlopes.io/tool-for-spring-mockmvcrequestbuilder-forms-tests/
这是一个 Kotlin SpringBoot 示例:
@RunWith(MockitoJUnitRunner::class)
class ApiFormControllerTest {
lateinit var mvc: MockMvc
@InjectMocks
lateinit var apiFormController: ApiFormController
@Before
fun setup() {
mvc = MockMvcBuilders.standaloneSetup(apiFormController).setControllerAdvice(ExceptionAdvice()).build()
}
fun MockHttpServletRequestBuilder.withForm(params: Map<String, String>): MockHttpServletRequestBuilder {
this.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.content(
EntityUtils.toString(
UrlEncodedFormEntity(
params.entries.toList().map { BasicNameValuePair(it.key, it.value) }
)
)
)
return this
}
@Test
fun canSubmitValidForm() {
mvc.perform(post("/forms").withForm(mapOf("subject" to "hello")))
.andExpect(status().isOk)
}
}
现代 spring (5.3.12) 提供的解决方案无效。使用 MockHttpServletRequestBuilder 中的 param 方法似乎有一个简单而优雅的解决方案:
mockMvc.perform(post("/some/super/secret/url")
.param("someparam1", true)
.param("someparam2", false)
.with(csrf())
).andExpect(status().isOk)
注意:因为我正在使用 Spring 安全性,所以我需要添加
.with(csrf())
以便 CSRF 后处理器允许我的请求。如果不是,Spring 安全将拒绝请求以避免 跨站点请求伪造 (CSRF) 攻击。