java - 使用 Mockito 测试 LoginController

标签 java spring spring-boot mockito junit4

我正在尝试编写测试以确保我的登录 Controller 具有某些模型属性并运行某些服务方法。

当我对此 Controller 运行测试时,仅分别在登录表单和注册表单测试中收到错误

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

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

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)

最佳答案

您的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();
}

关于java - 使用 Mockito 测试 LoginController,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43312014/

相关文章:

java - 通过重定向到操作来保留所有请求参数

java - 我正在尝试从 JAVA 中的 MultiMap(org.apache.commons.collections.MultiMap) 中删除单个值

java - 销毁方法在spring框架中不起作用

mysql - 链接现有域名后,DigitalOcean Droplet 上的 PHPmyAdmin 出现 404 Not Found 错误

java - 在java中的给定时间之间执行受控数量的线程

elasticsearch - 通过应用弹性查询从嵌套字段中获取数据

java - HEROKU - 无法使用 jdk11 部署 java 应用程序

java - 我可以仅使用 xhtml 文件声明复合组件并且仍然能够在 JAVA 代码中使用该组件吗

java - 在Spring Boot应用程序中从Enums动态创建列

java - 错误请求错误单元测试多部分 Spring 支架