1. 简介
在本教程中,我们将学习如何使用 RestAssuredMockMvc 对 Spring 的 REST 控制器进行测试。RestAssured 是一个基于 Java 的测试框架,其提供的 spring-mock-mvc
模块可以非常方便地测试 Spring MVC 应用中的 Controller 层。
我们会介绍两种测试模式:Standalone 模式 和 WebApplicationContext 模式,分别适用于单元测试和集成测试。
2. Maven 依赖
在开始写测试之前,我们需要在 pom.xml
中引入 spring-mock-mvc
模块:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
✅ 确保版本号与你使用的 REST-assured 主版本一致。
3. 初始化 RestAssuredMockMvc
初始化 RestAssuredMockMvc
是使用 REST-assured 的起点。支持两种初始化方式:
3.1 Standalone 模式
适用于单元测试,手动传入需要测试的 @Controller
或 @ControllerAdvice
实例。
单次初始化(推荐)
@Before
public void initialiseRestAssuredMockMvcStandalone() {
RestAssuredMockMvc.standaloneSetup(new CourseController());
}
每次测试前初始化(不推荐)
@Test
public void whenGetCourse() {
given()
.standaloneSetup(new CourseController())
//...
}
⚠️ 适合测试少量接口时使用,频繁初始化影响性能。
3.2 WebApplicationContext 模式
适用于集成测试,使用 Spring 的 WebApplicationContext
初始化。
静态初始化(推荐)
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void initialiseRestAssuredMockMvcWebApplicationContext() {
RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}
每次测试初始化(不推荐)
@Test
public void whenGetCourse() {
given()
.webAppContextSetup(webApplicationContext)
//...
}
✅ 推荐使用静态初始化,避免重复操作。
4. 被测系统(SUT)
我们以一个典型的 Spring Boot 项目为例,包含一个 REST 接口 CourseController
:
@SpringBootApplication
class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@RestController
@RequestMapping("/courses")
public class CourseController {
private final CourseService courseService;
public CourseController(CourseService courseService) {
this.courseService = courseService;
}
@GetMapping(produces = APPLICATION_JSON_UTF8_VALUE)
public Collection<Course> getCourses() {
return courseService.getCourses();
}
@GetMapping("/{code}")
public Course getCourse(@PathVariable String code) {
return courseService.getCourse(code);
}
}
class Course {
private String code;
// 构造方法、getter、setter 等
}
服务层与异常处理:
@Service
class CourseService {
private static final Map<String, Course> COURSE_MAP = new ConcurrentHashMap<>();
static {
Course wizardry = new Course("Wizardry");
COURSE_MAP.put(wizardry.getCode(), wizardry);
}
Collection<Course> getCourses() {
return COURSE_MAP.values();
}
Course getCourse(String code) {
return Optional.ofNullable(COURSE_MAP.get(code)).orElseThrow(() ->
new CourseNotFoundException(code));
}
}
异常处理器:
@ControllerAdvice(assignableTypes = CourseController.class)
public class CourseControllerExceptionHandler extends ResponseEntityExceptionHandler {
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(CourseNotFoundException.class)
public void handleCourseNotFoundException(CourseNotFoundException cnfe) {
// 空实现
}
}
class CourseNotFoundException extends RuntimeException {
CourseNotFoundException(String code) {
super(code);
}
}
5. 使用 REST-assured 进行单元测试
我们使用 JUnit + Mockito 来进行单元测试,结合 RestAssuredMockMvc
的 standalone 模式:
@RunWith(MockitoJUnitRunner.class)
public class CourseControllerUnitTest {
@Mock
private CourseService courseService;
@InjectMocks
private CourseController courseController;
@InjectMocks
private CourseControllerExceptionHandler courseControllerExceptionHandler;
@Before
public void initialiseRestAssuredMockMvcStandalone() {
RestAssuredMockMvc.standaloneSetup(courseController, courseControllerExceptionHandler);
}
✅ 使用 standalone 模式,只加载指定的 Controller,测试速度快。
示例测试:获取课程列表
@Test
public void givenNoExistingCoursesWhenGetCoursesThenRespondWithStatusOkAndEmptyArray() {
when(courseService.getCourses()).thenReturn(Collections.emptyList());
given()
.when()
.get("/courses")
.then()
.log().ifValidationFails()
.statusCode(OK.value())
.contentType(JSON)
.body(is(equalTo("[]")));
}
示例测试:获取不存在的课程返回 404
@Test
public void givenNoMatchingCoursesWhenGetCoursesThenRespondWithStatusNotFound() {
String nonMatchingCourseCode = "nonMatchingCourseCode";
when(courseService.getCourse(nonMatchingCourseCode)).thenThrow(
new CourseNotFoundException(nonMatchingCourseCode));
given()
.when()
.get("/courses/" + nonMatchingCourseCode)
.then()
.log().ifValidationFails()
.statusCode(NOT_FOUND.value());
}
✅ 使用 given-when-then 风格,结构清晰,可读性强。
6. 使用 REST-assured 进行集成测试
对于集成测试,我们使用完整的 Spring 上下文:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CourseControllerIntegrationTest {
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void initialiseRestAssuredMockMvcWebApplicationContext() {
RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}
示例测试:获取不存在的课程返回 404
@Test
public void givenNoMatchingCourseCodeWhenGetCourseThenRespondWithStatusNotFound() {
String nonMatchingCourseCode = "nonMatchingCourseCode";
given()
.when()
.get("/courses/" + nonMatchingCourseCode)
.then()
.log().ifValidationFails()
.statusCode(HttpStatus.NOT_FOUND.value());
}
✅ 使用 WebApplicationContext 模式,测试更贴近真实运行环境。
7. 小结
模式 | 适用场景 | 特点 |
---|---|---|
Standalone 模式 | 单元测试 | 只加载指定 Controller,速度快 |
WebApplicationContext 模式 | 集成测试 | 加载完整上下文,更真实 |
REST-assured 提供了 DSL 风格的 API,非常适合测试 RESTful 接口。对于 Spring MVC 项目,使用 RestAssuredMockMvc
可以非常方便地进行接口测试,且测试代码简洁易读。
完整代码示例请参考:GitHub 仓库