1. 概述
在本篇文章中,我们将深入了解 Jersey Test Framework,并探讨如何利用它快速编写集成测试。
正如我们之前所了解的,**Jersey 是一个用于开发 RESTful Web 服务的开源框架**。如果你对 Jersey 还不熟悉,可以先阅读我们的另一篇文章 使用 Jersey 和 Spring 构建 REST API 来入门。
2. 应用配置
Jersey Test Framework 是一个用于验证服务端组件实现是否正确的工具。正如你即将看到的,它提供了一种快速且无痛的方式来编写集成测试,并且能够很好地处理与 HTTP API 的通信。
✅ 同时,它几乎开箱即用,非常容易集成到基于 Maven 的项目中。虽然框架主要基于 JUnit,但也支持 TestNG,因此在大多数环境中都能使用。
接下来,我们将介绍如何在项目中添加必要的依赖。
2.1. Maven 依赖
首先,我们需要在 pom.xml
中添加 Jersey Test Framework 的核心依赖:
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>3.1.1</version>
<scope>test</scope>
</dependency>
最新版本可以从 Maven Central 获取。
几乎所有的 Jersey 测试都会使用默认的 Grizzly 容器工厂,因此我们还需要添加以下依赖:
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>3.1.1</version>
<scope>test</scope>
</dependency>
同样,最新版本可以从 Maven Central 获取。
3. 快速上手
在这一节中,我们将介绍编写一个简单测试所需的基本步骤。
我们从测试一个简单的 Greetings
接口开始:
@Path("/greetings")
public class Greetings {
@GET
@Path("/hi")
public String getHiGreeting() {
return "hi";
}
}
3.1. 配置测试类
现在我们来定义测试类:
public class GreetingsResourceIntegrationTest extends JerseyTest {
@Override
protected Application configure() {
return new ResourceConfig(Greetings.class);
}
//...
}
✅ 注意:要使用 Jersey Test Framework 编写测试,测试类必须继承 JerseyTest
类。
接着我们重写 configure
方法,返回一个自定义的资源配置对象,其中只包含我们要测试的 Greetings
接口。
3.2. 编写第一个测试
我们来测试一下这个接口的 GET 请求:
@Test
public void givenGetHiGreeting_whenCorrectRequest_thenResponseIsOkAndContainsHi() {
Response response = target("/greetings/hi").request()
.get();
assertEquals("Http Response should be 200: ", Status.OK.getStatusCode(), response.getStatus());
assertEquals("Http Content-Type should be: ", MediaType.TEXT_HTML, response.getHeaderString(HttpHeaders.CONTENT_TYPE));
String content = response.readEntity(String.class);
assertEquals("Content of ressponse is: ", "hi", content);
}
✅ 我们可以完全访问 HTTP 响应对象 —— 所以可以检查状态码是否成功,也可以读取响应体内容。
具体来说,上面的测试做了以下几件事:
- 向
/greetings/hi
发送 GET 请求 - 验证响应的状态码和
Content-Type
- 验证响应内容是否为
"hi"
4. 测试 GET 接口获取资源
现在我们已经了解了测试的基本流程,接下来测试一个简单的 Fruit API,这个 API 在之前的 Jersey MVC 支持 文章中已经介绍过。
4.1. 获取原始 JSON 响应
下面的例子中,我们将响应体当作普通的 JSON 字符串处理:
@Test
public void givenFruitExists_whenSearching_thenResponseContainsFruit() {
final String json = target("fruit/search/strawberry").request()
.get(String.class);
assertThat(json, containsString("{\"name\":\"strawberry\",\"weight\":20}"));
}
4.2. 获取映射后的实体对象
我们也可以将响应直接映射为实体类对象,例如:
@Test
public void givenFruitExists_whenSearching_thenResponseContainsFruitEntity() {
final Fruit entity = target("fruit/search/strawberry").request()
.get(Fruit.class);
assertEquals("Fruit name: ", "strawberry", entity.getName());
assertEquals("Fruit weight: ", Integer.valueOf(20), entity.getWeight());
}
这一次,我们在 get
方法中指定了目标类型,框架会自动将响应体转换为 Fruit
对象。
5. 测试 POST 接口创建资源
在 API 中创建新资源时,通常会使用 POST 请求。接下来我们将展示如何测试这部分接口。
5.1. 提交原始 JSON
我们先测试通过提交 JSON 字符串来创建一个新的 Fruit 资源:
@Test
public void givenCreateFruit_whenJsonIsCorrect_thenResponseCodeIsCreated() {
Response response = target("fruit/created").request()
.post(Entity.json("{\"name\":\"strawberry\",\"weight\":20}"));
assertEquals("Http Response should be 201 ", Status.CREATED.getStatusCode(), response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit saved : Fruit [name: strawberry colour: null]"));
}
✅ 这里我们使用了 Entity.json()
方法,它能方便地将 JSON 字符串封装为 Entity
对象。
5.2. 提交实体对象
正如 GET 请求一样,我们也可以直接提交实体对象:
@Test
public void givenCreateFruit_whenFruitIsInvalid_thenResponseCodeIsBadRequest() {
Fruit fruit = new Fruit("Blueberry", "purple");
fruit.setWeight(1);
Response response = target("fruit/create").request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(fruit, MediaType.APPLICATION_JSON_TYPE));
assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit weight must be 10 or greater"));
}
这次我们使用 Entity.entity()
方法提交 Fruit
实体对象,并指定了媒体类型为 JSON。
5.3. 使用 POST 提交表单
最后,我们来看一个使用 POST 提交表单的例子:
@Test
public void givenCreateFruit_whenFormContainsNullParam_thenResponseCodeIsBadRequest() {
Form form = new Form();
form.param("name", "apple");
form.param("colour", null);
Response response = target("fruit/create").request(MediaType.APPLICATION_FORM_URLENCODED)
.post(Entity.form(form));
assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit colour must not be null"));
}
这里我们使用 Entity.form()
方法提交了一个包含多个参数的表单。
6. 测试其他 HTTP 方法
有时候我们还需要测试 PUT 或 DELETE 等其他 HTTP 方法。✅ Jersey Test Framework 当然也支持这些操作。
下面是一个简单的 PUT 示例:
@Test
public void givenUpdateFruit_whenFormContainsBadSerialParam_thenResponseCodeIsBadRequest() {
Form form = new Form();
form.param("serial", "2345-2345");
Response response = target("fruit/update").request(MediaType.APPLICATION_FORM_URLENCODED)
.put(Entity.form(form));
assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit serial number is not valid"));
}
调用 request()
方法后,我们就可以调用任意 HTTP 方法了。
7. 附加功能
Jersey Test Framework 提供了一些额外的配置项,可以帮助调试和测试。
下面的例子展示了如何启用一些调试功能:
public class FruitResourceIntegrationTest extends JerseyTest {
@Override
protected Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
//...
✅ 我们可以在配置应用时启用这些功能,例如 LOG_TRAFFIC
和 DUMP_ENTITY
,它们会在测试运行时输出详细的请求/响应日志,非常实用。
8. 支持的容器
前面我们提到,Jersey Test Framework 默认使用 Grizzly 作为测试容器。✅ 但它也支持其他几种容器:
- In-Memory 容器
- Oracle JDK 自带的 HttpServer
- Simple 容器(org.simpleframework.http)
- Jetty 容器(org.eclipse.jetty)
更多关于如何配置这些容器的信息,请参考官方文档 这里。
9. 总结
本文中,我们探索了 Jersey Test Framework 的基本使用方式。首先介绍了如何配置框架,然后展示了如何为一个简单 API 编写测试。
接着我们测试了 GET 和 POST 接口,最后还介绍了框架的一些附加功能以及支持的容器选项。
✅ 完整源码可以在 GitHub 上获取:https://github.com/eugenp/tutorials/tree/master/web-modules/jersey