我正在彻底学习Spring Boot,我已经接近尾声了,这似乎是最后一个症结点。
我觉得我没有得到的是那些如此简单和直接的事情之一,它甚至没有解释。
无论如何,我有一个简单的 CRUD 演示,其中有一个表单,用于创建硬编码的实体,一旦我提交表单,就会重定向到显示创建的新实体的页面。
它在多次测试的网络浏览器和邮递员中完美运行,但由于某种原因,当我执行@WebMvcTest和@SpringBootTest时,我得到了正确的302响应状态,但重定向响应中没有响应主体。
我确实读过这个问题: https://stackoverflow.com/a/45555607/21505152
即使主要文档也缺少任何信息: https://docs.spring.io/spring-framework/reference/testing/spring-mvc-test-framework/vs-end-to-end-integration-tests.html
这基本上表明 MockMvc 可能无法测试此类场景,如果是这样,有什么替代方案? 我还需要测试这个吗?
发生什么事了?因为一切都在浏览器中完美运行
这是代码:
@Controller
@RequestMapping("/dummy-entities")
public class DummyEntityController {
private DummyEntityDao dao;
public DummyEntityController(DummyEntityDao dao) {
this.dao = dao;
}
@GetMapping("")
public String getAll(Model model) {
model.addAttribute("mainHeading", "Dummy Entities");
model.addAttribute("dummyEntities", dao.getDummyEntities());
return "dummyentities/dummy-entities";
}
@GetMapping("/{id}")
public String getById(@PathVariable int id, Model model) {
try {
model.addAttribute("dummyEntity", dao.getDummyEntityById(id));
return "dummyentities/dummy-entity";
}catch (DummyEntityNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
}
}
@GetMapping("/add")
public String getAddDummyEntityView() {
return "dummyentities/add";
}
@PostMapping("/add")
public String addDummyEntity(@ModelAttribute DummyEntity dummyEntity) {
DummyEntity newDummyEntity = dao.createDummyEntity(dummyEntity);
return "redirect:/dummy-entities/" + newDummyEntity.getId();
}
@GetMapping("/edit/{id}")
public String getEditDummyEntityView(@PathVariable int id, Model model) {
try {
DummyEntity dummyEntity = dao.getDummyEntityById(id);
model.addAttribute("dummyEntity", dummyEntity);
return "dummyentities/edit";
}catch (DummyEntityNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
}
}
@PostMapping("/edit/{id}")
public String editDummyEntity(@ModelAttribute DummyEntity dummyEntity) {
try {
dao.updateDummyEntityById(dummyEntity);
}catch (DummyEntityWriteException e) {
}
return "redirect:/dummy-entities/" + dummyEntity.getId();
}
@RequestMapping("/delete/{id}")
public String deleteDummyEntity(Model model, @PathVariable int id) {
try {
dao.deleteDummyEntityById(id);
model.addAttribute("deletedDummyEntityId", id);
return "dummyentities/delete";
}catch (DummyEntityNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
}catch (DummyEntityWriteException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
}
}
}
class DummyEntityControllerIntegratedTest extends MvcThymeleafApplicationTests {
@Test
void createAddsOne() throws Exception {
DummyEntity dummyEntity = new DummyEntity(0, "DE-13", "dek13", "dev13", 10, 20, 30);
mockMvc.perform(
MockMvcRequestBuilders.post("/dummy-entities/add")
.param("name", "DE-13")
.param("key", "dek13")
.param("value", "dev13")
.param("x", Integer.valueOf(10).toString())
.param("y", Integer.valueOf(20).toString())
.param("z", Integer.valueOf(30).toString())
).andExpect(result -> assertEquals(302, result.getResponse().getStatus()));
}
@Test
void createRedirectsToDehome () throws Exception {
DummyEntity dummyEntity = new DummyEntity(0, "DE-13", "dek13", "dev13", 10, 20, 30);
Map<String, Object> model = mockMvc.perform(
MockMvcRequestBuilders.post("/dummy-entities/add")
.param("name", "DE-13")
.param("key", "dek13")
.param("value", "dev13")
.param("x", Integer.valueOf(10).toString())
.param("y", Integer.valueOf(20).toString())
.param("z", Integer.valueOf(30).toString())
).andReturn().getModelAndView().getModel();
DummyEntity newDummyEntity = (DummyEntity) model.get("dummyEntity");
assertNotNull(newDummyEntity);
}
}
它在浏览器中运行完美,但第二次测试失败。 在我看来,它正在被重定向,这意味着客户端应该再次向获取新 id 的端点发出请求,并且应该返回这个?
我没有尝试使用 RedirectAttributes,因为我不确定它是否必要,如果这模拟了一个新请求,那么当它到达控制器端点时,一切都应该正常进行?
我尝试调试,但我发现重定向的端点在测试期间永远不会被调用,但我认为它可以工作,因为浏览器中的最终结果是完美的,它只是在这些测试中不起作用,这是为什么?
我将不胜感激任何帮助
您必须自己进行重定向。我为此创建了一个助手。
@Autowired
private MockMvc mvc;
@Test
void myMvcTest() throws Exception {
MvcResult mvcResult = mvc.perform(
MockMvcRequestBuilders
.post("/dummy-entities/add")
// ...
).andExpect(status().is(302)).andReturn();
String responseBody = mvcResult.getResponse().getContentAsString();
assertThat(responseBody.indexOf("Find a Text")).isPositive();
}
帮手
import static org.junit.jupiter.api.Assertions.fail;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
public class MockMvcUtility {
public static MvcResult performRedirect(MockMvc mvc, MvcResult mvcResult) {
try {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders
.get(mvcResult.getResponse().getRedirectedUrl());
return mvc.perform(builder).andReturn();
} catch (Exception e) {
fail(e.getMessage());
return null;
}
}
}