创建一个 Controller 类
package com.example.testingweb;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@GetMapping("/")
public String greeting() {
return "Hello, World";
}
}
如何测试这个 Controller
直接注入对应的 HomeController
@SpringBootTest
public class HomeControllerTest {
@Autowired
private HomeController homeController;
@Test
public void should_return_hello_world() {
String expected = "Hello, World";
String actual = homeController.greeting();
assertEquals(expected, actual);
}
}
启动一个随机端口进行测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class HttpRequestTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void should_return_hello_world() {
String expected = "Hello, World";
String actual = restTemplate.getForObject("http://localhost:" + port + "/", String.class);
assertEquals(expected, actual);
}
}
使用 WebEnvironment.RANDOM_PORT
在测试时启动一个真实的服务器,但使用随机端口。这种方式有以下优点:
- 避免端口冲突:在 CI/CD 环境中特别有用,因为多个测试可能同时运行
- 更接近生产环境:测试会通过实际的 HTTP 请求和响应
- 配置简单:使用
@LocalServerPort
自动注入随机生成的端口号 - 自动装配:Spring Boot 会自动提供配置好的
TestRestTemplate
实例,只需通过@Autowired
注入即可
这种测试方式适合于需要验证完整 HTTP 请求-响应周期的场景。
不启动服务器进行测试
Spring Boot 提供了第三种测试方式,即在不启动实际服务器的情况下测试 Web 层。这种方法使用 Spring 的 MockMvc
,通过模拟 HTTP 请求来测试控制器。测试会涵盖从 HTTP 请求到控制器的完整调用链路,但省去了启动服务器的开销。要使用这种方式,只需在测试类上添加 @AutoConfigureMockMvc
注解,并注入 MockMvc
实例即可。
这种方法的优点是:
- 测试执行速度更快,因为不需要启动服务器
- 可以直接测试 Spring MVC 的各层面功能
- 提供了详细的请求处理结果断言支持
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class HomeControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void should_return_hello_world() throws Exception {
String expected = "Hello, World";
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(result -> {
String actual = result.getResponse().getContentAsString();
assertEquals(expected, actual);
});
}
}
仅测试 Web 层
在这个测试中,启动了完整的 Spring 应用程序上下文,但没有启动服务器。我们可以使用@WebMvcTest
将测试范围缩小到仅 Web 层
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(HomeController.class)
public class HomeControllerMvcTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(status().isOk())
.andExpect(content().string("Hello, World"));
}
}
这种测试方式使用的断言方法与前面的 MockMvc 测试相似,但有一个重要区别:使用 @WebMvcTest
时,Spring Boot 只实例化 Web 层组件,而不是整个应用程序上下文。这种方式特别适用于大型应用,因为你可以通过 @WebMvcTest(HomeController.class)
这样的方式指定要测试的具体控制器,从而进一步提高测试效率。
注意如果 Controller 依赖其他的 bean,需要使用 MockBean,创建这个依赖的 bean,否则会启动失败。