Pet*_*ete 56 java testing spring integration-testing spring-mvc
在集成测试spring mvc web应用程序时,有没有办法在模拟请求上传递整个表单对象?我能找到的就是将每个字段分别作为这样的参数传递:
mockMvc.perform(post("/somehwere/new").param("items[0].value","value"));
Run Code Online (Sandbox Code Playgroud)
这对于小型表格来说很好.但是,如果我发布的对象变大了怎么办?如果我可以发布整个对象,它也会使测试代码看起来更好.
具体来说,我想通过复选框测试多个项目的选择,然后发布它们.当然我可以测试发布一个项目,但我想知道..
我们使用spring 3.2.2和spring-test-mvc.
我的表单模型看起来像这样:
NewObject {
List<Item> selection;
}
Run Code Online (Sandbox Code Playgroud)
我试过这样的电话:
mockMvc.perform(post("/somehwere/new").requestAttr("newObject", newObject)
Run Code Online (Sandbox Code Playgroud)
到这样的控制器:
@Controller
@RequestMapping(value = "/somewhere/new")
public class SomewhereController {
@RequestMapping(method = RequestMethod.POST)
public String post(
@ModelAttribute("newObject") NewObject newObject) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
但是对象将是空的(是的,我在测试之前填充了它)
我发现唯一可行的解决方案就是使用@SessionAttribute: Spring MVC应用程序的集成测试:表单
但我不喜欢在我需要的每个控制器的末尾都要记得调用完成的想法.在所有表单数据不必在会话中之后,我只需要它来处理一个请求.
所以我现在唯一能想到的就是编写一些Util类,它使用MockHttpServletRequestBuilder将所有对象字段附加为.param,使用反射或单独为每个测试用例添加..
我不知道,觉得不直观..
关于如何让我变得更轻松的任何想法/想法?(除了直接调用控制器)
谢谢!
nyx*_*yxz 57
我有同样的问题,结果证明解决方案相当简单,使用JSON marshaller.
让控制器只需更改为更改签名@ModelAttribute("newObject")
即可@RequestBody
.像这样:
@Controller
@RequestMapping(value = "/somewhere/new")
public class SomewhereController {
@RequestMapping(method = RequestMethod.POST)
public String post(@RequestBody NewObject newObject) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
然后在你的测试中你可以简单地说:
NewObject newObjectInstance = new NewObject();
// setting fields for the NewObject
mockMvc.perform(MockMvcRequestBuilders.post(uri)
.content(asJsonString(newObjectInstance))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON));
Run Code Online (Sandbox Code Playgroud)
凡asJsonString
方法就是:
public static String asJsonString(final Object obj) {
try {
final ObjectMapper mapper = new ObjectMapper();
final String jsonContent = mapper.writeValueAsString(obj);
return jsonContent;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Run Code Online (Sandbox Code Playgroud)
axt*_*avt 23
集成测试的主要目的之一MockMvc
是验证模型对象是否与表单数据相关联.
为了做到这一点,你必须传递表单数据,因为它们是从实际表单(使用.param()
)传递的.如果您使用某些自动转换来自NewObject
数据,则您的测试将不会涵盖特定类别的可能问题(NewObject
与实际形式不兼容的修改).
Mic*_*ris 20
我相信我使用Spring Boot 1.4获得了最简单的答案,包括测试类的导入:
public class SomeClass { /// this goes in it's own file
//// fields go here
}
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.test.web.servlet.MockMvc
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
@RunWith(SpringRunner.class)
@WebMvcTest(SomeController.class)
public class ControllerTest {
@Autowired private MockMvc mvc;
@Autowired private ObjectMapper mapper;
private SomeClass someClass; //this could be Autowired
//, initialized in the test method
//, or created in setup block
@Before
public void setup() {
someClass = new SomeClass();
}
@Test
public void postTest() {
String json = mapper.writeValueAsString(someClass);
mvc.perform(post("/someControllerUrl")
.contentType(MediaType.APPLICATION_JSON)
.content(json)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
Run Code Online (Sandbox Code Playgroud)
rev*_*ind 10
我认为大多数这些解决方案都太复杂了.我假设在你的测试控制器中你有这个
@Autowired
private ObjectMapper objectMapper;
Run Code Online (Sandbox Code Playgroud)
如果是休息服务
@Test
public void test() throws Exception {
mockMvc.perform(post("/person"))
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(new Person()))
...etc
}
Run Code Online (Sandbox Code Playgroud)
对于使用已发布表单的spring mvc,我想出了这个解决方案.(还不确定它是不是一个好主意)
private MultiValueMap<String, String> toFormParams(Object o, Set<String> excludeFields) throws Exception {
ObjectReader reader = objectMapper.readerFor(Map.class);
Map<String, String> map = reader.readValue(objectMapper.writeValueAsString(o));
MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
map.entrySet().stream()
.filter(e -> !excludeFields.contains(e.getKey()))
.forEach(e -> multiValueMap.add(e.getKey(), (e.getValue() == null ? "" : e.getValue())));
return multiValueMap;
}
@Test
public void test() throws Exception {
MultiValueMap<String, String> formParams = toFormParams(new Phone(),
Set.of("id", "created"));
mockMvc.perform(post("/person"))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.params(formParams))
...etc
}
Run Code Online (Sandbox Code Playgroud)
基本思想是 - 首先将对象转换为json字符串以轻松获取所有字段名称 - 将此json字符串转换为映射并将其转储到MultiValueMap
该spring期望的字段中.(可选)过滤掉您不想包含的任何字段(或者您可以只注释字段@JsonIgnore
以避免此额外步骤)
小智 5
使用Reflection解决的另一种方法,但没有编组:
我有这个抽象的助手类:
public abstract class MvcIntegrationTestUtils {
public static MockHttpServletRequestBuilder postForm(String url,
Object modelAttribute, String... propertyPaths) {
try {
MockHttpServletRequestBuilder form = post(url).characterEncoding(
"UTF-8").contentType(MediaType.APPLICATION_FORM_URLENCODED);
for (String path : propertyPaths) {
form.param(path, BeanUtils.getProperty(modelAttribute, path));
}
return form;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
你这样使用它:
// static import (optional)
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
// in your test method, populate your model attribute object (yes, works with nested properties)
BlogSetup bgs = new BlogSetup();
bgs.getBlog().setBlogTitle("Test Blog");
bgs.getUser().setEmail("admin.localhost@example.com");
bgs.getUser().setFirstName("Administrator");
bgs.getUser().setLastName("Localhost");
bgs.getUser().setPassword("password");
// finally put it together
mockMvc.perform(
postForm("/blogs/create", bgs, "blog.blogTitle", "user.email",
"user.firstName", "user.lastName", "user.password"))
.andExpect(status().isOk())
Run Code Online (Sandbox Code Playgroud)
我推断,在构建表单时能够提及属性路径会更好,因为我需要在测试中改变它.例如,我可能想检查是否在丢失的输入上得到验证错误,并且我将省略属性路径来模拟条件.我还发现在@Before方法中构建我的模型属性更容易.
BeanUtils来自commons-beanutils:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
<scope>test</scope>
</dependency>
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
81417 次 |
最近记录: |