如何使用 Spring MVC 测试来测试表单提交?

How do I test form submission with Spring MVC test?

我使用 Spring 创建控制器的大部分经验都是针对使用 JSON 格式请求的 REST 控制器。我一直在寻找关于如何对表单提交进行测试的文档,到目前为止,这是我理解它应该使用 MockMvc:

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

但是,我不确定如何将表单参数映射到模型对象。我已经看到 @ModelAttribute 注释在我的搜索中弹出,但我无法弄清楚它应该如何用于映射。此外,官方文档中的 this quick start guide 并未详细说明 th:objectth:field 等内容如何转换为 HTML 以及随后的 URL 编码形式。

我的控制器代码类似于以下内容:

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

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

当您使用 @ModelAttribute 标记 writeUp 对象时,Spring 容器会填充参数(如 titledescription 等。 ) from HttpServletRequest object & inject the object to the controller method, when the request comes to the server from client (can be a Browser or MockMvc unit test client or anything).

此外,还有一些其他的基本要点供您快速理解:

(1) Controller 方法映射到 URI 和 RequestMethod(如 POST/GET/DELETE/PUT 等),如下所示:

    @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 将被映射到一个对象(比如你的 writeUp)用于 http POST/PUT 请求,其中 html formd 数据是 http 正文的一部分.

(3) @RequestParam@PathParam 将用于 http GET 请求,其中参数是 URL 的一部分(即,不是 http 正文的一部分)。

您可以查看 here 以了解 DispatcherServlet 请求处理和 Spring MVC 基本 Web 流程。

我通过反复试验发现我的特定问题可能是 Groovy 特定的。事实证明,测试代码和控制器代码没有问题。重申一下,为了测试表单提交,使用 param 方法到 MockMvcRequestBuildersperform 方法。另一件需要注意的事情是,如果未指定内容类型,这似乎不起作用。这是一个适用于我的示例测试代码:

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

如您所见,它与我最初发布的内容没有太大区别。控制器代码几乎完全相同,@ModelAttribute 工作正常。

我的设置的问题是 因为我使用的是 Groovy,所以我假设 getter 和 setter 是在我的 WriteUp class。这是 WriteUp class 最初的样子:

class WriteUp {
    private String title
    private String description
}

我已经有一段时间没有在 Groovy 中编写代码了,上次我写的时候,可以假定 class 像上面的代码一样隐式地具有 getter 和 setter。然而,事实并非如此。 为了解决我的具体问题,我将字段中的访问修饰符更新为 default(包级别):

class WriteUp {
    String title
    String description
}