使用Mockito测试LoginController

时间:2017-04-09 21:15:05

标签: 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: This may be the result of an unspecified view, due to default view name generation.)

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.)

但是当我运行其他控制器(主控制器)时,我没有收到这些错误!

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;
    }
}

LoginController.java

@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";
    }
}

LoginControllerTest.java

@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)

1 个答案:

答案 0 :(得分:1)

您的ViewResolver未用于测试。您必须使用当前的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();
}

或者使用ViewResolver您只需要更改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();
}