Psycho Punch
Psycho Punch

Reputation: 6892

How do I test form submission with Spring MVC test?

Most of my experience with creating controllers with Spring are for REST controllers that consume JSON formatted requests. I've been searching for documentation on how to do testing for form submission, and so far this is how I understand it should go using MockMvc:

MvcResult result = mockMvc.perform(post("/submit")
                .param('title', 'test title')
                .param('description', 'test description'))
                .andReturn()

However, I'm not sure how to map the form parameters to a model object. I've seen the @ModelAttribute annotation pop up in my searches but I can't figure out how it should be used for mapping. In addition, this quick start guide from the official documentation does not elaborate on how things like th:object and th:field translate to HTML and subsequently to URL encoded form.

I have my controller code similar to the following:

@PostMapping('/submit')
def submit(@ModelAttribute WriteUp writeUp) {
    //do something with writeUp object
    'result'
}

Upvotes: 2

Views: 5429

Answers (2)

Psycho Punch
Psycho Punch

Reputation: 6892

I discovered through trial and error that my specific problem might have been Groovy specific. There test code and the controller code, it turns out, have no issues. To reiterate, for testing form submission, use the param method through perform method of MockMvcRequestBuilders. Another thing to note is that this doesn't seem to work if content type is not specified. Here's a sample test code that works for me:

MvcResult result = webApp.perform(post("/submit")
        .contentType(APPLICATION_FORM_URLENCODED) //from MediaType
        .param('title', 'test title')
        .param('description', 'test description'))
        .andReturn()

As you can see, it's not much different from what I posted originally. The controller code is pretty much just the same, with @ModelAttribute working just fine.

The problem with my setup though was that since I was using Groovy, I assumed that getters and setters were automatically generated in my WriteUp class. Here's how the WriteUp class looked originally:

class WriteUp {
    private String title
    private String description
}

I haven't written code in Groovy for a while, and the last time I did, classes like the one above can be assumed to have getters and setters implicitly. However, it turns out that is not the case. To solve my specific issue, I updated the access modifier in the fields to be default (package level):

class WriteUp {
    String title
    String description
}

Upvotes: 5

Vasu
Vasu

Reputation: 22402

I've seen the @ModelAttribute annotation pop up in my searches but I can't figure out how it should be used for mapping.

When you mark your writeUp object with @ModelAttribute, then the Spring container populates the parameters (like title, description, etc..) from HttpServletRequest object & injects the object to the controller method, when the request comes to the server from the client (could be a Browser or MockMvc unit test client or anything else).

Also, few other basic points for your quick understanding:

(1) Controller methods are mapped to an URI and RequestMethod (like POST/GET/DELETE/PUT et..) like shown below:

    @RequestMapping(value="/submit", method=RequestMethod.POST)
    public String submit(@ModelAttribute WriteUp writeUp) {

       //Call the service and Save the details

        model.addAttribute("Writeup details added successfully");

        return "writeUpResult"; //Returns to the View (JSP)
     }

(2) @ModelAttribute will be mapped to an object (like your writeUp) for http POST/PUT requests where the html formd data is part of http body.

(3) @RequestParam or @PathParam will be used for http GET requests where the parameters are part of URL (i.e., not part of http body).

You can look here for understanding the DispatcherServlet request handling & Spring MVC basic web flow.

Upvotes: 0

Related Questions