Pregunta

I am using MockMvc with Mockito, drawing heavily on Spring greenhouse sample. I have a simple controller which returns a String viewname, or null if there is some sort of error. It works well in servlet environment, however when I try to unit test the controller with MockMvc standaloneSetup, I get

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

This is my Controller

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

}

and a failed test:

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

A simple walkaround is returning "/register.do" instead of null for fails in the controller, but I'd rather not change my code to make tests work.

¿Fue útil?

Solución

My guess is the reason for this difference is because in production you have configured your ViewResolver to use "/WEB-INF/" and possibly a suffix. In your standalone test setup you are not adding a ViewResolver so the default simply takes the view name and turns it into a path, at which point it realizes it is the same as the request path.

As far as doc for .do you have a point and we'll be adding something soon, track here.

Otros consejos

Ok, after a few hours of breaking my head, I got it.

Basically, it will give this issue as long as the initial url is the same as the end url. However you can fool it by asking to post("register.do") (which is what the servlet does), and check that it returns "register". Thus, my solution was:

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

And now it works in both test and server environments.

P.S. I wish there was good documention for all this .do stuff. I never truly understood it, after years of working with it.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top