1. 介紹
在本教程中,我們將學習如何使用 RestAssuredMockMvc 測試我們的 Spring REST 控制器。 RestAssuredMockMvc 是基於 Spring 的 MockMvc 構建的 REST-assured API。
首先,我們將探討不同的配置選項。然後,我們將深入瞭解如何編寫單元測試和集成測試。
本教程使用了 Spring MVC、Spring MockMVC 和 REST-assured,因此請務必查看這些教程。
2. Maven 依賴
在開始編寫測試之前,我們需要將 io.rest-assured:spring-mock-mvc 模塊 導入到我們的 Maven <em title="pom.xml">pom.xml</em> 中:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>3. 初始化 RestAssuredMockMvc
接下來,我們需要初始化 RestAssuredMockMvc,這是 DSL 的起點,無論是在 獨立模式還是 Web 應用程序上下文模式下。
在兩種模式下,我們可以隨時隨地執行它,也可以靜態地一次性配置。讓我們來看一些示例。
3.1. 獨立模式
在獨立模式下,我們使用 RestAssuredMockMvc,並使用一個或多個 @Controller 或 @ControllerAdvice 註解的類。
如果只有少量測試,我們可以隨時初始化 RestAssuredMockMvc。
@Test
public void whenGetCourse() {
given()
.standaloneSetup(new CourseController())
//...
}不過,如果測試數量很多,直接進行一次靜態分析會更容易:
@Before
public void initialiseRestAssuredMockMvcStandalone() {
RestAssuredMockMvc.standaloneSetup(new CourseController());
}3.2. Web Application Context
在 Web Application Context 模式下,我們使用 RestAssuredMockMvc 實例初始化 WebApplicationContext</em/>。
類似於我們在 standalone 模式下進行的設置,我們可以隨時在每個測試中初始化 RestAssuredMockMvc</em/>。
@Autowired
private WebApplicationContext webApplicationContext;
@Test
public void whenGetCourse() {
given()
.webAppContextSetup(webApplicationContext)
//...
}或者,我們也可以靜態地一次性完成它:
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void initialiseRestAssuredMockMvcWebApplicationContext() {
RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}4. 系統測試對象 (SUT)
在開始查看一些示例測試之前,我們需要一個測試對象。讓我們先查看我們的系統測試對象,從我們的 @SpringBootApplication 配置開始:
@SpringBootApplication
class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}接下來,我們有一個簡單的 <em><a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RestController.html">@RestController</a></em>,它暴露了我們的Course領域:
@RestController
@RequestMapping(path = "/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(path = "/{code}", produces = APPLICATION_JSON_UTF8_VALUE)
public Course getCourse(@PathVariable String code) {
return courseService.getCourse(code);
}
}class Course {
private String code;
// usual contructors, getters and setters
}並且,最不容忽視的是,我們的服務類和 @ControllerAdvice 用於處理我們的 CourseNotFoundException:
@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);
}
}現在我們已經有了用於測試的系統,讓我們來查看一些 RestAssuredMockMvc 測試用例。
5. 使用 REST-assured 進行 REST Controller 單元測試
我們可以使用 RestAssuredMockMvc 與我們喜歡的測試工具 JUnit 和 Mockito 一起測試我們的 @RestController。
首先,我們 mock 並構造我們的系統下被測試單元 (SUT),然後像上面一樣,以獨立模式初始化 RestAssuredMockMvc:
@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);
}由於我們在 @Before 方法中靜態初始化了 RestAssuredMockMvc,因此無需在每個測試中單獨初始化它。
獨立模式非常適合單元測試,因為它只會初始化我們提供的控制器,而不是整個應用程序上下文。 這樣可以保持我們的測試速度。
現在,讓我們來看一個示例測試:
@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("[]")));
}初始化 RestAssuredMockMvc</em/>,同時結合我們的 @ControllerAdvice</em/> 和 @RestController</em/>,使我們能夠測試我們的異常場景:
@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());
}如上所示,REST-assured 使用了熟悉的“給定-然後-校驗”場景格式來定義測試:
- given() — 指定 HTTP 請求的詳細信息
- when() — 指定 HTTP 動詞以及路由
- then() — 驗證 HTTP 響應
6. 使用 REST-assured 進行 REST Controller 集成測試
我們可以使用 RestAssuredMockMvc 與 Spring 的測試工具,用於我們的集成測試。
首先,我們設置測試類,使用 @RunWith(SpringRunner.class) 以及 @SpringBootTest(webEnvironment = RANDOM_PORT)。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
public class CourseControllerIntegrationTest {
//...
}這將使用在我們的 類中配置的應用程序上下文運行我們的測試,並在隨機端口上進行。
接下來,我們注入 WebApplicationContext 並使用它,如上所述,初始化 RestAssuredMockMvc。
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void initialiseRestAssuredMockMvcWebApplicationContext() {
RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}現在我們已經設置好測試類並且 RestAssuredMockMvc 初始化完畢,我們就可以開始編寫測試用例了:
@Test
public void givenNoMatchingCourseCodeWhenGetCourseThenRespondWithStatusNotFound() {
String nonMatchingCourseCode = "nonMatchingCourseCode";
given()
.when()
.get("/courses/" + nonMatchingCourseCode)
.then()
.log().ifValidationFails()
.statusCode(NOT_FOUND.value());
}請記住,由於我們在 @Before 方法中靜態初始化了 RestAssuredMockMvc,因此在每個測試中無需再次初始化它。
要更深入地瞭解 REST-assured API,請查看 REST-assured 指南。
7. 結論
在本教程中,我們學習瞭如何使用 REST-assured 測試我們的 Spring MVC 應用程序,並利用 REST-assured 的 <em>spring-mock-mvc</em> 模塊。
使用 <em>RestAssuredMockMvc</em> 在 <strong>獨立模式</strong>下進行單元測試非常方便,因為它只會初始化提供的Controller`,從而保持測試速度。
使用 <em>RestAssuredMockMvc</em> 在 <strong>Web 應用程序上下文模式</strong>下進行集成測試,因為它可以利用完整的WebApplicationContext`。