Spring 使用 TestRestTemplate 的启动控制器测试总是失败,端点返回 404

Spring Boot controller test with TestRestTemplate always fails with endpoints returning 404

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

目前,由于端点返回 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"
                }
            """)
    }
}

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

It is a requirement that only what is absolutely required for the controller be included in the test context,...

SpringBoot 已经为此提供了工具 - 请参阅 @WebMvcTest slice documentation or

如您的要求所述:

It is a requirement that only what is absolutely required for the controller be included in the test context, so spinning up the entire application context for the test is not an option.

您应该考虑使用 @WebMvcTest 仅测试 Web 层。使用当前 @SpringBootTest 和随机端口,您将启动整个 Spring 上下文并启动嵌入式 Tomcat。使用 @WebMvcTest 您可以注入一个 MockMvc 实例并在响应 body/header/status.

上编写断言

一个 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()))
  }
}

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)