Возвращая ноль в прерываниях контроллера MockMvc
Я использую MockMvc с Mockito, опираясь в основном на весенний образец теплицы. У меня есть простой контроллер, который возвращает String viewname или null, если есть какая-то ошибка. Это хорошо работает в среде сервлетов, однако, когда я пытаюсь выполнить модульное тестирование контроллера с помощью MockMvc standaloneSetup, я получаю
javax.servlet.ServletException: Circular view path [register]: would dispatch back to the current handler URL [/register] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
Это мой контроллер
@Controller
public class SignupController {
@RequestMapping(value="/register", method=RequestMethod.GET)
public RegistrationForm createNewRegForm(){
return new RegistrationForm();
}
/**
* Process a signup form submission.
* Delegate to a {@link SignupServiceImpl} to actually complete the signin transaction.
* Redirects the new member to the application home page on successful sign-in.
*/
@RequestMapping(value="/register", method=RequestMethod.POST)
public String signup(@Validated(Registration.class) RegistrationForm form, BindingResult formBinding) {
if(formBinding.hasErrors())
return null;
boolean success = signupHelper.signup(form.getUser(), formBinding);
return (!formBinding.hasErrors() && success) ? "redirect:/" : null;
}
}
и проваленный тест:
public void signup_duplicateEmail() throws Exception {
AccountManager accountRepository = mock(AccountManager.class);
AccountAuthUtils authorization = mock(AccountAuthUtils.class);
when(accountRepository.createAccount(any(User.class))).thenThrow(new EmailAlreadyOnFileException("roy@clarkson.com"));
SignupServiceImpl gateway = new SignupServiceImpl(accountRepository, authorization);
SignupController signupController = new SignupController(gateway);
MockMvc mockMvc = standaloneSetup(signupController).build();
mockMvc.perform(post("/register").contentType(APPLICATION_FORM_URLENCODED)
.param("user.username","habuma")
.param("user.email","roy%40clarkson.com")
.param("user.password", "letmein1")
.param("confirmPassword", "letmein1"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.forwardedUrl("/register.do"))
.andExpect(MockMvcResultMatchers.model().hasErrors())
.andExpect(MockMvcResultMatchers.model().attributeHasFieldErrors("registrationForm", "user.email"));
}
Простой обход кода возвращает "/register.do" вместо null для сбоев в контроллере, но я бы предпочел не менять свой код, чтобы тесты работали.
2 ответа
Я предполагаю, что причина этого различия заключается в том, что в производственном процессе вы настроили свой ViewResolver для использования "/WEB-INF/" и, возможно, суффикса. В вашей автономной тестовой настройке вы не добавляете ViewResolver, поэтому по умолчанию просто берет имя представления и превращает его в путь, после чего он понимает, что он совпадает с путем запроса.
Что касается документации по.do, у вас есть точка зрения, и мы скоро что-нибудь добавим, следите здесь.
Хорошо, после нескольких часов, когда я сломал голову, я понял.
В основном, это даст эту проблему, пока начальный URL совпадает с конечным URL. Однако вы можете обмануть его, попросив опубликовать ("register.do") (что и делает сервлет) и проверить, что он возвращает "register". Таким образом, мое решение было:
mockMvc.perform(post("/register.do").contentType(APPLICATION_FORM_URLENCODED)
.param("user.username","habuma")
.param("user.email","roy%40clarkson.com")
.param("user.password", "letmein1")
.param("confirmPassword", "letmein1"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.forwardedUrl("register"))
И теперь он работает как в тестовой, так и в серверной среде.
PS Хотелось бы, чтобы была вся документация для всего этого.do. Я никогда по-настоящему не понимал этого после многих лет работы с ним.