1. 概览
在本文中我们将讨论如何使用 REST-assured 测试 RESTful 服务,并重点关注如何获取和校验 REST APIs 的响应。
2. 测试类设置
在之前的文章中已经讨论过了 REST-assured 的基本使用,并展示了如何操作请求头、cookie 和 请求参数。
在现有的基础上,我们添加了一个简单的 REST 控制器 AppController,它在内部调用服务 AppService。我们将在测试示例中使用这些类。
要创建测试类,我们需要做更多的设置。由于我们在 classpath 中有 spring-boot-starter-test 依赖,所以我们可以轻松地使用 spring Testing 来测试。
首先,让我们创建AppControllerIntegrationTest类的框架:
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class AppControllerIntegrationTest { @LocalServerPort private int port; private String uri; @PostConstruct public void init() { uri = "http://localhost:" + port; } @MockBean AppService appService; //test cases } 复制代码
在这个 JUnit 测试中,我们用几个 Spring 注解来标注我们的类,这些注解会在本地随机选取可用的端口启动应用程序。在 @PostConstruct 中,我们获取了完整 URI 来进行 REST 接口调用。
我们还在 AppService 上使用了 @MockBean,因为我们需要模拟这个类上的方法调用。
3. 断言 JSON 响应
JSON 是 REST API 中用于交换数据的最常见格式。响应可以由单个 JSON 对象或 JSON 对象数组组成。我们将在本节中见到这两种类型。
3.1 单个 JSON 对象
假设我们需要测试 /movie/{id},如果找到,它将返回一个 movie JSON 对象。 我们将使用 Mockito 框架模拟 AppService 调用以返回一些数据:
@Test public void givenMovieId_whenMakingGetRequestToMovieEndpoint_thenReturnMovie() { Movie testMovie = new Movie(1, "movie1", "summary1"); when(appService.findMovie(1)).thenReturn(testMovie); get(uri + "/movie/" + testMovie.getId()).then() .assertThat() .statusCode(HttpStatus.OK.value()) .body("id", equalTo(testMovie.getId())) .body("name", equalTo(testMovie.getName())) .body("synopsis", notNullValue()); } 复制代码
上述代码中我们首先模拟了 appService.findMovie(1) 调用返回一个 Movie 对象。然后,我们在 REST-assured 的 get() 方法中构造了 REST URL,实现 GET 请求。最后,我们做了四个断言。
首先,我们检查了响应码,然后检查了 body 的内容。我们使用 Hamcrest 来断言预期值。
还要注意,如果响应 JSON 是嵌套的,我们可以使用点操作符 “key1.key2.key3” 来获取嵌套的键的值。
3.2 校验之后提取 JSON 响应
在某些情况下,我们可能需要在断言之后在提取响应内容,以便对其执行其他操作。我们可以使用extract() 方法提取 JSON 响应为一个类:
Movie result = get(uri + "/movie/" + testMovie.getId()).then() .assertThat() .statusCode(HttpStatus.OK.value()) .extract() .as(Movie.class); assertThat(result).isEqualTo(testMovie); 复制代码
上述代码中我们使用 REST-assured 直接提取 JSON 响应为一个 Movie 对象并对它进行断言。
我们也可以提取整个 JSON 响应为一个字符串,使用 extract().asString() 方法:
String responseString = get(uri + "/movie/" + testMovie.getId()).then() .assertThat() .statusCode(HttpStatus.OK.value()) .extract() .asString(); assertThat(responseString).isNotEmpty(); 复制代码
我们也可以从 JSON 响应中提取特定的部分内容。
让我们看一个 POST API的测试,它需要一个 Movie JSON主体,如果插入成功,将返回相同的内容:
@Test public void givenMovie_whenMakingPostRequestToMovieEndpoint_thenCorrect() { Map<String, String> request = new HashMap<>(); request.put("id", "11"); request.put("name", "movie1"); request.put("synopsis", "summary1"); int movieId = given().contentType("application/json") .body(request) .when() .post(uri + "/movie") .then() .assertThat() .statusCode(HttpStatus.CREATED.value()) .extract() .path("id"); assertThat(movieId).isEqualTo(11); } 复制代码
上述代码中我们首先创建了 POST 请求所需的请求数据。然后,我们使用 path() 方法从返回的 JSON 响应中提取 id 字段。
3.3 JSON 对象数组
我们也可以对 JSON 对象数组进行校验:
@Test public void whenCallingMoviesEndpoint_thenReturnAllMovies() { Set<Movie> movieSet = new HashSet<>(); movieSet.add(new Movie(1, "movie1", "summary1")); movieSet.add(new Movie(2, "movie2", "summary2")); when(appService.getAll()).thenReturn(movieSet); get(uri + "/movies").then() .statusCode(HttpStatus.OK.value()) .assertThat() .body("size()", is(2)); } 复制代码
我们再次首先 模拟了 appService.getAll(),并发出请求。然后我们断言了 statusCode 和响应数组的大小。
这同样可以通过提取 JSON 响应内容完成:
Movie[] movies = get(uri + "/movies").then() .statusCode(200) .extract() .as(Movie[].class); assertThat(movies.length).isEqualTo(2); 复制代码
4. 校验 Headers 和 Cookies
我们也可以对响应的 Headers 和 Cookies 进行断言:
@Test public void whenCallingWelcomeEndpoint_thenCorrect() { get(uri + "/welcome").then() .assertThat() .header("sessionId", notNullValue()) .cookie("token", notNullValue()); } 复制代码
同样可以单独提取 headers 和 Cookies 的内容:
Response response = get(uri + "/welcome"); String headerName = response.getHeader("sessionId"); String cookieValue = response.getCookie("token"); assertThat(headerName).isNotBlank(); assertThat(cookieValue).isNotBlank(); 复制代码
5. 校验文件
如果 REST API 返回一个文件,我们可以使用 asByteArray() 方法提取响应:
File file = new ClassPathResource("test.txt").getFile(); long fileSize = file.length(); when(appService.getFile(1)).thenReturn(file); byte[] result = get(uri + "/download/1").asByteArray(); assertThat(result.length).isEqualTo(fileSize); 复制代码
这里,我们首先模拟了 appService.getFile(1) 返回 src/test/resources 路径中的文本文件。然后,我们调用服务并提取响应中的 byte[] ,然后断言该响应具有预期值。
6. 总结
在本文中,我们研究了使用 REST-assured 从 REST API 获取和验证响应的不同方式。
本文中的代码可以在 Github上获得。