【问题标题】:Spring Boot controller test with TestRestTemplate always fails with endpoints returning 404使用 TestRestTemplate 的 Spring Boot 控制器测试总是失败,端点返回 404
【发布时间】:2020-06-15 22:34:51
【问题描述】:

我正在尝试使用 TestRestTemplate 创建一个 Spring Boot 控制器的测试。要求只有控制器绝对需要的内容才能包含在测试上下文中,因此为测试启动整个应用程序上下文不是一种选择。

由于端点返回 404,当前测试失败。端点在生产中正常工作。控制器似乎没有注册到 web servlet。

控制器显示如下:

@RestController
class MyController {
    @GetMapping("/endpoint")
    fun endpoint(): ResponseDto {
        return ResponseDto(data = "Some data")
    }
}

data class ResponseDto(val data: String)

测试如下:

@SpringBootTest(
    classes = [MyController::class, ServletWebServerFactoryAutoConfiguration::class],
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
internal class MyControllerTestRestTemplateTest(
    @Autowired private val restTemplate: TestRestTemplate
) {
    @Test
    fun `should work`() {
        val result = restTemplate.getForEntity("/endpoint", String::class.java)

        result.body.shouldMatchJson(
            """
                {
                    "data": "Some data"
                }
            """)
    }
}

我怎样才能让这个测试设置工作?

【问题讨论】:

  • 您能否尝试从测试中的注释中删除ServletWebServerFactoryAutoConfiguration

标签: spring-boot kotlin spring-boot-test


【解决方案1】:

要求只有控制器绝对需要的内容才能包含在测试上下文中,...

SpringBoot 已经为此提供了工具 - 请参阅 @WebMvcTest slice documentationthis SO answer

【讨论】:

  • 谢谢。我曾尝试使用@WebMvcTest,但遇到了不同的问题。尽管如此,我最终还是能够解决这些问题,所以现在有了一个完整的解决方案。
【解决方案2】:

根据您的要求:

要求只有绝对需要的 控制器被包含在测试上下文中,所以旋转整个 测试的应用程序上下文不是一个选项。

您应该考虑使用 @WebMvcTest 仅测试 Web 层。使用您当前的@SpringBootTest 和随机端口,您将启动整个 Spring 上下文并启动嵌入式 Tomcat。使用@WebMvcTest,您可以注入MockMvc 实例并在响应正文/标题/状态上写入断言。

Java 示例可能如下所示

@WebMvcTest(MyController.class)
class MyControllerTests {

    @Autowired
    private MockMvc mvc;

    @Test
    void testExample() throws Exception {
        this.mvc.perform(get("/endpoint")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string("YOUR_STRING_HERE"));
    }
}

一个有效的 Kotlin 示例如下所示

@WebMvcTest(MyController::class)
internal class MyControllerTests(@Autowired private val mockMvc: MockMvc) {

  @Test
  fun testExample() {
    this.mockMvc.perform(MockMvcRequestBuilders.get("/endpoint")
      .accept(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk)
      .andExpect(content().json("""
        {
         "data": "Some data"
        }
      """.trimIndent()))
  }
}

【讨论】:

    【解决方案3】:

    rieckpil 和 Josef 的回答都是正确的,我同意使用 @WebMvcTest 是一种更好的方法。

    如果你坚持使用@SpringBootTest 和TestRestTemplate:你使用的是webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT。这意味着您的 TestRestTemplate 不知道要使用哪个端口。您需要在字符串 url 中包含整个 url,包括应用程序正在运行的端口

    通过添加

    @LocalServerPort
    int randomServerPort = 0
    

    然后提供完整的url

    val result = restTemplate.getForEntity("http://localhost:${randomServerPort}/endpoint", String::class.java)
    

    【讨论】:

    • 感谢您的建议。我试过了,但不幸的是我仍然得到一个 404。它似乎使用了正确的端口,但不知何故没有向容器注册控制器。
    猜你喜欢
    • 1970-01-01
    • 2017-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-15
    • 2018-02-20
    • 1970-01-01
    • 2022-12-09
    相关资源
    最近更新 更多