Java 使用Mockito测试LoginController
我正在尝试编写测试,以确保我的登录控制器具有某些模型属性并运行某些服务方法 当我运行这个控制器的测试时,我得到一个错误,分别是loginForm和registerForm测试Java 使用Mockito测试LoginController,java,spring,spring-boot,mockito,junit4,Java,Spring,Spring Boot,Mockito,Junit4,我正在尝试编写测试,以确保我的登录控制器具有某些模型属性并运行某些服务方法 当我运行这个控制器的测试时,我得到一个错误,分别是loginForm和registerForm测试 javax.servlet.ServletException: Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: T
javax.servlet.ServletException: Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
及
但是当我运行我的另一个控制器(主控制器)时,我没有得到这些错误
TemplateConfig.java
@Configuration
public class TemplateConfig {
@Bean
public SpringResourceTemplateResolver templateResolver() {
final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setCacheable(false);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
springTemplateEngine.addTemplateResolver(templateResolver());
springTemplateEngine.addDialect(new SpringSecurityDialect());
return springTemplateEngine;
}
@Bean
public ThymeleafViewResolver viewResolver() {
final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
return viewResolver;
}
}
@Controller
public class LoginController {
//todo: create test
@Autowired
private UserService userService;
@RequestMapping(path = "/login", method = RequestMethod.GET)
public String loginForm(Model model, HttpServletRequest request) {
model.addAttribute("user", new User());
try {
Object flash = request.getSession().getAttribute("flash");
model.addAttribute("flash", flash);
request.getSession().removeAttribute("flash");
} catch (Exception ex) {}
return "login";
}
@RequestMapping(path = "/register", method = RequestMethod.GET)
public String registerForm(Model model){
UserDto user = new UserDto();
model.addAttribute("user", user);
return "register";
}
//todo: add test
@RequestMapping(path = "/register", method = RequestMethod.POST)
public String createUser(@ModelAttribute("user") @Valid UserDto userDto, BindingResult bindingResult, RedirectAttributes redirectAttributes){
//todo: add autologin
if(bindingResult.hasErrors()){
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.user", bindingResult);
return "redirect:/register";
}
//todo: add tests for exceptions
try {
userService.registerNewUserAccount(userDto);
} catch (UsernameAlreadyExistsException ex) {
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("flash", new FlashMessage(ex.getMessage(), FlashMessage.Status.FAILURE));
return "redirect:/register";
} catch (EmailAlreadyExistsException ex) {
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("flash", new FlashMessage(ex.getMessage(), FlashMessage.Status.FAILURE));
return "redirect:/register";
}
return "redirect:/login";
}
@ExceptionHandler(RoleNotFoundException.class)
public String roleNotFound(Model model, Exception ex){
model.addAttribute("ex", ex);
return "error";
}
}
@RunWith(MockitoJUnitRunner.class)
public class LoginControllerTest {
private MockMvc mockMvc;
@InjectMocks
private LoginController controller;
@Mock
private UserService service;
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void loginForm_ShouldIncludeNewUserInModel() throws Exception {
mockMvc.perform(get("/login"))
.andExpect(status().isOk())
.andExpect(view().name("login"))
.andExpect(model().attribute("user", instanceOf(User.class)));
}
@Test
public void registerForm_ShouldIncludeNewUserDtoInModel() throws Exception {
mockMvc.perform(get("/register"))
.andExpect(status().isOk())
.andExpect(view().name("register"))
.andExpect(model().attribute("user", instanceOf(UserDto.class)));
}
@Test
public void createUser_ShouldCreateUserWithValidCredentials() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/login"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldRedirectToRegisterWhenUsernameAlreadyExistsExceptionIsThrown() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
//Can't use when method since service.registerNewUserAccount return type is void
doThrow(new UsernameAlreadyExistsException()).when(service).registerNewUserAccount(any(UserDto.class));
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/register"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldRedirectToRegisterWhenEmailAlreadyExistsExceptionIsThrown() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
//Can't use when method since service.registerNewUserAccount return type is void
doThrow(new EmailAlreadyExistsException()).when(service).registerNewUserAccount(any(UserDto.class));
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/register"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldIncludeNewUserDtoAndBindingResultInModelWhenValidationErrors() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("dsa")
.withUsername("d")
.withPassword(" ")
.build();
mockMvc.perform(post("/register").requestAttr("user", userDto))
.andExpect(view().name("register"))
.andExpect(status().isOk())
.andExpect(model().attribute("user", new UserDto()))
.andExpect(model().attribute("org.springframework.validation.BindingResult.user", instanceOf(BindingResult.class)));
}
}
LoginController.java
@Configuration
public class TemplateConfig {
@Bean
public SpringResourceTemplateResolver templateResolver() {
final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setCacheable(false);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
springTemplateEngine.addTemplateResolver(templateResolver());
springTemplateEngine.addDialect(new SpringSecurityDialect());
return springTemplateEngine;
}
@Bean
public ThymeleafViewResolver viewResolver() {
final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
return viewResolver;
}
}
@Controller
public class LoginController {
//todo: create test
@Autowired
private UserService userService;
@RequestMapping(path = "/login", method = RequestMethod.GET)
public String loginForm(Model model, HttpServletRequest request) {
model.addAttribute("user", new User());
try {
Object flash = request.getSession().getAttribute("flash");
model.addAttribute("flash", flash);
request.getSession().removeAttribute("flash");
} catch (Exception ex) {}
return "login";
}
@RequestMapping(path = "/register", method = RequestMethod.GET)
public String registerForm(Model model){
UserDto user = new UserDto();
model.addAttribute("user", user);
return "register";
}
//todo: add test
@RequestMapping(path = "/register", method = RequestMethod.POST)
public String createUser(@ModelAttribute("user") @Valid UserDto userDto, BindingResult bindingResult, RedirectAttributes redirectAttributes){
//todo: add autologin
if(bindingResult.hasErrors()){
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.user", bindingResult);
return "redirect:/register";
}
//todo: add tests for exceptions
try {
userService.registerNewUserAccount(userDto);
} catch (UsernameAlreadyExistsException ex) {
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("flash", new FlashMessage(ex.getMessage(), FlashMessage.Status.FAILURE));
return "redirect:/register";
} catch (EmailAlreadyExistsException ex) {
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("flash", new FlashMessage(ex.getMessage(), FlashMessage.Status.FAILURE));
return "redirect:/register";
}
return "redirect:/login";
}
@ExceptionHandler(RoleNotFoundException.class)
public String roleNotFound(Model model, Exception ex){
model.addAttribute("ex", ex);
return "error";
}
}
@RunWith(MockitoJUnitRunner.class)
public class LoginControllerTest {
private MockMvc mockMvc;
@InjectMocks
private LoginController controller;
@Mock
private UserService service;
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void loginForm_ShouldIncludeNewUserInModel() throws Exception {
mockMvc.perform(get("/login"))
.andExpect(status().isOk())
.andExpect(view().name("login"))
.andExpect(model().attribute("user", instanceOf(User.class)));
}
@Test
public void registerForm_ShouldIncludeNewUserDtoInModel() throws Exception {
mockMvc.perform(get("/register"))
.andExpect(status().isOk())
.andExpect(view().name("register"))
.andExpect(model().attribute("user", instanceOf(UserDto.class)));
}
@Test
public void createUser_ShouldCreateUserWithValidCredentials() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/login"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldRedirectToRegisterWhenUsernameAlreadyExistsExceptionIsThrown() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
//Can't use when method since service.registerNewUserAccount return type is void
doThrow(new UsernameAlreadyExistsException()).when(service).registerNewUserAccount(any(UserDto.class));
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/register"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldRedirectToRegisterWhenEmailAlreadyExistsExceptionIsThrown() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
//Can't use when method since service.registerNewUserAccount return type is void
doThrow(new EmailAlreadyExistsException()).when(service).registerNewUserAccount(any(UserDto.class));
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/register"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldIncludeNewUserDtoAndBindingResultInModelWhenValidationErrors() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("dsa")
.withUsername("d")
.withPassword(" ")
.build();
mockMvc.perform(post("/register").requestAttr("user", userDto))
.andExpect(view().name("register"))
.andExpect(status().isOk())
.andExpect(model().attribute("user", new UserDto()))
.andExpect(model().attribute("org.springframework.validation.BindingResult.user", instanceOf(BindingResult.class)));
}
}
LoginControllerTest.java
@Configuration
public class TemplateConfig {
@Bean
public SpringResourceTemplateResolver templateResolver() {
final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setCacheable(false);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
springTemplateEngine.addTemplateResolver(templateResolver());
springTemplateEngine.addDialect(new SpringSecurityDialect());
return springTemplateEngine;
}
@Bean
public ThymeleafViewResolver viewResolver() {
final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
return viewResolver;
}
}
@Controller
public class LoginController {
//todo: create test
@Autowired
private UserService userService;
@RequestMapping(path = "/login", method = RequestMethod.GET)
public String loginForm(Model model, HttpServletRequest request) {
model.addAttribute("user", new User());
try {
Object flash = request.getSession().getAttribute("flash");
model.addAttribute("flash", flash);
request.getSession().removeAttribute("flash");
} catch (Exception ex) {}
return "login";
}
@RequestMapping(path = "/register", method = RequestMethod.GET)
public String registerForm(Model model){
UserDto user = new UserDto();
model.addAttribute("user", user);
return "register";
}
//todo: add test
@RequestMapping(path = "/register", method = RequestMethod.POST)
public String createUser(@ModelAttribute("user") @Valid UserDto userDto, BindingResult bindingResult, RedirectAttributes redirectAttributes){
//todo: add autologin
if(bindingResult.hasErrors()){
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.user", bindingResult);
return "redirect:/register";
}
//todo: add tests for exceptions
try {
userService.registerNewUserAccount(userDto);
} catch (UsernameAlreadyExistsException ex) {
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("flash", new FlashMessage(ex.getMessage(), FlashMessage.Status.FAILURE));
return "redirect:/register";
} catch (EmailAlreadyExistsException ex) {
redirectAttributes.addFlashAttribute("user", new UserDto());
redirectAttributes.addFlashAttribute("flash", new FlashMessage(ex.getMessage(), FlashMessage.Status.FAILURE));
return "redirect:/register";
}
return "redirect:/login";
}
@ExceptionHandler(RoleNotFoundException.class)
public String roleNotFound(Model model, Exception ex){
model.addAttribute("ex", ex);
return "error";
}
}
@RunWith(MockitoJUnitRunner.class)
public class LoginControllerTest {
private MockMvc mockMvc;
@InjectMocks
private LoginController controller;
@Mock
private UserService service;
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void loginForm_ShouldIncludeNewUserInModel() throws Exception {
mockMvc.perform(get("/login"))
.andExpect(status().isOk())
.andExpect(view().name("login"))
.andExpect(model().attribute("user", instanceOf(User.class)));
}
@Test
public void registerForm_ShouldIncludeNewUserDtoInModel() throws Exception {
mockMvc.perform(get("/register"))
.andExpect(status().isOk())
.andExpect(view().name("register"))
.andExpect(model().attribute("user", instanceOf(UserDto.class)));
}
@Test
public void createUser_ShouldCreateUserWithValidCredentials() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/login"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldRedirectToRegisterWhenUsernameAlreadyExistsExceptionIsThrown() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
//Can't use when method since service.registerNewUserAccount return type is void
doThrow(new UsernameAlreadyExistsException()).when(service).registerNewUserAccount(any(UserDto.class));
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/register"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldRedirectToRegisterWhenEmailAlreadyExistsExceptionIsThrown() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("example@gmail.com")
.withUsername("username12")
.withPassword("password12")
.build();
//Can't use when method since service.registerNewUserAccount return type is void
doThrow(new EmailAlreadyExistsException()).when(service).registerNewUserAccount(any(UserDto.class));
mockMvc.perform(
post("/register")
.requestAttr("user", userDto)
).andExpect(redirectedUrl("/register"));
verify(service).registerNewUserAccount(any(UserDto.class));
}
@Test
public void createUser_ShouldIncludeNewUserDtoAndBindingResultInModelWhenValidationErrors() throws Exception {
UserDto userDto = new UserDto.UserDtoBuilder()
.withEmail("dsa")
.withUsername("d")
.withPassword(" ")
.build();
mockMvc.perform(post("/register").requestAttr("user", userDto))
.andExpect(view().name("register"))
.andExpect(status().isOk())
.andExpect(model().attribute("user", new UserDto()))
.andExpect(model().attribute("org.springframework.validation.BindingResult.user", instanceOf(BindingResult.class)));
}
}
例外情况
14:44:31.138 [main] DEBUG org.springframework.web.servlet.view.InternalResourceView - Added model object 'user' of type [com.smarttodo.model.User] to request in view with name 'null'
14:44:31.138 [main] DEBUG org.springframework.web.servlet.view.InternalResourceView - Removed model object 'flash' from request in view with name 'null'
14:44:31.138 [main] DEBUG org.springframework.web.servlet.view.InternalResourceView - Added model object 'org.springframework.validation.BindingResult.user' of type [org.springframework.validation.BeanPropertyBindingResult] to request in view with name 'null'
14:44:31.140 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Error rendering view [org.springframework.web.servlet.view.InternalResourceView: unnamed; URL [login]] in DispatcherServlet with name ''
javax.servlet.ServletException: Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
at org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:205)
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:145)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1282)
at org.springframework.test.web.servlet.TestDispatcherServlet.render(TestDispatcherServlet.java:105)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:155)
at com.smarttodo.web.controller.LoginControllerTest.loginForm_ShouldIncludeNewUserInModel(LoginControllerTest.java:57)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
您的
viewsolver
未用于测试。您必须使用当前的ApplicationContext
,或者在测试中指定ViewResolver
使用ApplicationContext
可以将以下内容添加到LoginControllerTest.java中,请注意,我将其更改为使用SpringJUnit4ClassRunner
运行
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={TemplateConfig.class})
@WebAppConfiguration
@Autowired
WebApplicationContext wac;
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
或者使用viewsolver
只需更改setUp()
方法即可
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/view/");
viewResolver.setSuffix(".jsp");
mockMvc = MockMvcBuilders.standaloneSetup(new LoginController())
.setViewResolvers(viewResolver)
.build();
}
欢迎来到堆栈溢出!我想这段代码的95%与您的问题无关。请创建一个演示您的问题的URL。发生这种情况是因为您的映射方法正在返回它们来自的URL。换句话说,他们在呼唤自己。我该如何解决这个问题?代码实际上在运行时有效,只是测试不起作用。第二个答案很好,但第一个答案不行,因为当我为
@RunWith(MockitoJUnitRunner.class)
删除@RunWith(SpringJUnit4ClassRunner.class)
时,我不断收到断言错误,因为mockito无法运行。