java - 通过(伪)直接调用该处理程序方法来测试 Spring Controller - 好还是坏?如何实现?

标签 java spring spring-boot spring-mvc testing

一句话:我希望 MockMvc 的执行就像我直接调用 Controller 一样。

(P.S.这是语法糖。这并不意味着集成测试时我真的在调用 Controller 。)

<小时/>

详细信息:

假设我们有一个 Restful Controller :

class BookController {
    public Book updateBook(int id, Book newBook) {...}
}

典型的Spring integration testing RESTful 服务如下所示:

mockMvc.perform(put("/books/1")
                .content("{\"id\":1, \"name\": \"ABC\", ...}")
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON))
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id", is(1)))
                .andExpect(jsonPath("$.name", is("ABC")))
                ...and more...;

但是,我们可以做这样的事情:

BookController magicBookController = SomeMagic.generate_the_magic_controller();
Book result = magicMvc.perform(magicBookController.updateBook(1, new Book("ABC", ...)));
assertThat(1, result.getId());
assertThat("ABC", result.getName());
...

编辑:上面的代码不是简单地调用new BookController().updateBook(...)方法!我希望的是:generate_the_magic_controller将生成动态代理(使用cglib)。然后,当我们调用magicBookController.updateBook时,实际上动态生成的代码如下所示:

Book dynamically_generated_updateBook(int id, Book book) {
    String url = magic_assemble_url(id); // will become: "/books/1"
    String content = magic_assemble_content(book); // will become: "{name: AAA, ...}"
    Something result = mockMvc.perform(put(url)
                .content(content)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON));
    return parse_result(result); //parse back into book
}
<小时/>

一句话:我希望 MockMvc 的执行就像我直接调用 Controller 。我的问题:

  1. 我可以这么做吗? (或者这是一个非常糟糕的做法?)
  2. 怎么做?我正在考虑破解 Spring 框架中有关“查找和解析 Controller ”的部分,但对于如何做到这一点没有具体的想法......

编辑:我的测试目标如下。最初,人们喜欢用眼睛测试代码(给出输入并查看输出并用他们的思想进行断言)。当然,这很糟糕。因此,我们写下诸如“发布到/books 并断言结果是正确的”之类的内容。这就是我想测试的。恕我直言,它实际上有点像 E2E 测试(因为这是一项安静的服务)。 (或者我的目标完全错误?)

<小时/>

编辑: Jersey 的一个典型测试,我认为(个人)比 Spring 测试更优雅:

Profile profile = resources.getJerseyTest()
                              .target("/v1/profile/" + AuthHelper.VALID_NUMBER_TWO)
                              .request()
                              .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
                              .get(Profile.class);
assertEquals(profile.getXXX(), "aaa");
...

非常感谢您的任何想法!

最佳答案

MockMvc 的要点是使用 spring-mvc 基础设施测试 REST API 是否正确配置,例如可以将 HTTP 请求映射到并使用正确的参数调用预期的 Controller 方法,从 Controller 方法返回的 java 对象可以序列化为 HTTP 响应中正确的 JSON 主体等。它是关于测试 REST API 在给定 HTTP 请求的情况下是否正确运行,因此如果要使用,则必须指定 HTTP 请求MockMvc

在我看来,您想测试您的其余客户端,例如它可以将请求发送到具有预期正文的预期 URL,并且它可以将 JSON 响应反序列化回 java 对象等,而不是测试如果您的 REST API 配置正确。如果是,您可以使用 Wiremockokhttp MockWebServer 等工具对 REST API 进行 stub ,然后照常使用其余客户端来调用 stub API。

所以,首先要问自己的是,您实际上想测试什么?通常我们每次只专注于测试一件事。最好直接以声明方式指定测试输入,而不涉及任何复杂的代码转换,例如将某些给定输入转换为测试真正需要的输入,因为更多转换代码意味着更多机会引入另一个方面的错误。

因此,如果您想测试是否正确配置了 spring-mvc API(即使用mockMvc),那么实现将生成的其余客户端转换为调用 API 实际需要的 HTTP 请求的东西是一个坏主意.

关于java - 通过(伪)直接调用该处理程序方法来测试 Spring Controller - 好还是坏?如何实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60585076/

相关文章:

java - Cloud SQL 代理和权限不足

multithreading - 如何自定义SecurityContextPersistenceFilter的行为?

java - 如何修复 Junit 和 Mockito 中的 'Argument(s) are different! Wanted' 错误

java - 尝试显示消息框时,NetBeans IDE 6.9.1 中的 Glassfish 服务器停止工作

java - Joda dateTime 解析器不必要地添加毫秒

java - JDBC 中大型 IN 子句的替代方案,如何使用 Java 中的表值参数?

spring - 与一对多 Spring Data JPA 相加

java - 使用 XSLT 从 XML 生成 EXCEL

java - 访问按键分组的缓存元素

java - 如何在 Spring 3 MVC 中执行触发过滤器的@CustomAnnotation 注释?