如何使用Rest Assured进行RESTful API测试

本文最后更新于:2024年11月22日 下午 04:43:14

RESTful API

随着互联网和移动设备的发展,人们对Web应用的使用需求也增加,传统的动态页面(如JSP)由于低效率而渐渐被HTML+JavaScript(Ajax)的前后端分离所取代,并且安卓、IOS、小程序等形式客户端层出不穷,客户端的种类出现多元化,而客户端和服务端就需要接口进行通信,但接口的规范性是一个问题。

所以一套结构清晰、符合标准、易于理解、扩展方便让大部分人都能够理解接受的接口风格就显得越来越重要,而RESTful风格的接口(RESTful API)刚好有以上特点,就逐渐被实践应用而变得流行起来,可以参考GitHub REST API documentation - GitHub Docs的文档对REST API进行学习。

REST并没有一个明确的标准,而更像是一种设计的风格,满足这种设计风格的程序或接口我们称之为RESTful(从单词字面来看就是一个形容词)。所以RESTful API 就是满足REST架构风格的接口。如果用一句话大概概括一下REST风格,那就是URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。 此处只简单介绍REST API,因为这不是讨论的重点,关于REST API设计规范和限制条件,需要在日常开发中了解学习。

HTTP请求方法和状态码

REST API使用五种HTTP方法来发送请求:

  • GET:在特定的URL上检索信息。
  • PUT:如果资源存在,则更新先前的资源,若不存在,则创建新的信息。
  • POST:向服务器发送信息,例如上传数据或创建新的实体。
  • DELETE:删除特定URL上的所有当前资源。
  • PATCH:用于对资源进行部分更新。

当使用以上方法发送请求后,客户端会收到数字形式的代码,称为“状态码”,有时也叫“响应码”。然后我们可以根据这些状态码来解读服务器对特定请求发送的响应。状态码主要分为五类,具体如下:

  • 1xx(100-199):信息,服务器收到请求,需要请求者继续执行操作
  • 2xx(200-299):成功,操作被成功接收并处理
  • 3xx(300-399):重定向,需要进一步的操作以完成请求
  • 4xx(400-499):客户端错误,请求包含语法错误或无法完成请求
  • 5xx(500-599):服务器错误,服务器在处理请求的过程中发生了错误,无法完成请求

通过以上状态码,我们可以判断应用程序的运行情况。1xx、2xx和3xx状态码通常不被视为错误,而是提供了一些信息性的消息,它们并不会影响用户体验。

但是,如果收到了4xx或5xx状态码,则表示发生了错误。这意味着用户/APP在访问API时可能会遇到错误消息。4xx状态码通常与客户端或浏览器级别的错误相关,而5xx状态码通常表示服务器级别的错误。因此在进行REST API测试时,应该通过检查这些错误代码来评估每个响应的情况。

有关HTTP协议的具体内容细节,可以查阅。HTTP | MDN (mozilla.org)

示例

以toolsqa网站的一个demo作为例子,我们可以在这里Swagger UI (demoqa.com)看到该示例提供的一些API,这个例子是一个书店的库存管理系统,提供了多种REST API方法来获取书店中书的信息。

Swagger 是一个实现了OpenAPI 规范的工具集,SwaggerUI是其生成的文档/页面。

在SwaggerUI中我们还可以对接口进行执行,这里的GET方法返回的是Books的具体内容。

使用Rest Assured进行REST API测试

Rest Assured是一个Java库,用于测试RESTful API。它被广泛应用于测试基于JSON和XML的Web应用程序。此外,它完全支持GET、PUT、POST、PATCH和DELETE等所有REST方法。接下来详细介绍如何使用Rest Assured库测试一个REST API。

要编写一个示例的REST API测试,我们将使用以下REST API链接。

  • 请求URL:https://demoqa.com/BookStore/v1/Books

  • HTTP方法:GET

  • 备注:此URL将返回书店的库存详情。请求中没有输入参数。

  • 响应:{“books”: [{“isbn”: “string”,”title”: “string”,”subTitle”: “string”,”author”:”string”,”publish_date”: “2022-01-25T13:44:50.276Z”,”publisher”: “string”,”pages”: 0,”description”: “string”,”website”: “string”}]}

实际上,如果我们直接在浏览器中打开上述URL,我们会得到如下所示的输出:

要使用Rest Assured库以编程方式获取相同的输出,我们需要按照以下步骤操作:

  1. 使用RestAssured类生成一个针对URL的RequestSpecification。
  2. 指定HTTP方法类型(GET方法)。
  3. 将请求发送到服务器。
  4. 从服务器获取响应。
  5. 打印返回的响应体。

以下是执行以上步骤的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import io.restassured.RestAssured;
import io.restassured.http.Method;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;

public class RestAssuredAPITest {

@Test
public void GetBooksDetails() {
// Specify the base URL to the RESTful web service
RestAssured.baseURI = "https://demoqa.com/BookStore/v1/Books";
// Get the RequestSpecification of the request to be sent to the server.
RequestSpecification httpRequest = RestAssured.given();
// specify the method type (GET) and the parameters if any.
//In this case the request does not take any parameters
Response response = httpRequest.request(Method.GET, "");
// Print the status and message body of the response received from the server
System.out.println("Status received => " + response.getStatusLine());
System.out.println("Response=>" + response.prettyPrint());

}
}

这样一来,我们就能够发起测试 API 调用,并从对应的接口获取响应了。

现在我们来简单说明一下对应的代码:

1
2
// 指定 RESTful Web 服务的基本 URL
RestAssured.baseURI = "https://demoqa.com/BookStore/v1/Books";

上述代码使用 RestAssured 类来设定基本 URI。在这里,基本 URI 是 “https://demoqa.com/BookStore/v1/Books”。基本 URI 指示了我们即将从服务器请求的资源的根地址(因此称为基本 URI)。然后,当我们在随后的代码中实际发起请求时,我们会添加参数(如果有的话)。

io.restassured.RestAssured 类是我们进行测试时所发起的任何类型的 HTTP 请求的基础。该类的一些关键特性包括:

  • 使用基本 URI 生成 HTTP 请求。
  • 提供支持以创建不同的 HTTP 方法类型(GET、POST、PUT、PATCH、DELETE、UPDATE、HEAD 和 OPTIONS)的请求。
  • 使用 HTTP 与服务器进行通信,并将测试中创建的请求发送到服务器。
  • 接收来自服务器的响应。
  • 提供支持验证从服务器接收到的响应。
  • io.restassured.RestAssured类内部使用了一个基于 Groovy 语言的 HTTP 客户端 HTTP builder 库。
1
2
3
4
5
// 获取要发送到服务器的请求的RequestSpecification
RequestSpecification httpRequest = RestAssured.given();
// 指定方法类型(GET)和参数(如果有的话)。
// 在这种情况下,请求不带任何参数。
Response response = httpRequest.request(Method.GET, "");

接下来的代码获取了要发送到服务器的请求的RequestSpecification对象。Rest Assured库提供了名为RequestSpecification的接口,用于此目的。变量httpRequest存储了请求,以便我们可以在需要时修改它,比如添加身份验证信息、添加头部等。在这个示例测试中,我们没有对该变量进行修改。

现在,我们调用接口以获取资源,以上代码使用request方法向服务器发送资源请求。

request方法接受两个参数,第一个是HTTP方法,第二个是一个字符串。字符串参数用于指定要与基本URI一起发送的参数。在这种情况下,为了获取Books的详情,我们不发送任何参数,因此使用了空字符串。request方法的返回类型是Response对象,这意味着request方法从服务器获取了响应并且把结果存在response这个实例中。

Response接口(io.restassured.response.Response)表示从服务器返回的响应。它包含服务器发送的所有数据。接下来的例子中可以调用此响应对象上的不同方法来提取响应,例如响应状态、header等。

1
2
3
// 打印从服务器接收到的响应消息正文
System.out.println("Status received => " + response.getStatusLine());
System.out.println("Response=>" + response.prettyPrint());

在上述代码行中,我们将响应作为字符串读取,并将其打印到System.out。我们使用响应接口的getBody方法返回响应的body。然后将其打印到System.out。

我们也可以使用Rest Assured提供的简写方法来编写上述测试代码。以下是稍微简化了的代码段。

1
2
3
4
5
6
7
8
9
10
11
@Test
public void GetWeatherDetailsCondensed() {
// 指定RESTful web服务的基本URL
RestAssured.baseURI = "https://demoqa.com/BookStore/v1/Books";
// 获取要发送的请求的RequestSpecification
RequestSpecification httpRequest = RestAssured.given();
// 调用RequestSpecification.get()方法以获取响应。
Response response = httpRequest.get("");
// Response.asString方法将直接返回主体的内容 // 作为字符串
System.out.println("Response Body is => " + response.asString());
}

在这里使用了RequestSpecification对象上的“get”方法,该方法向接口发送了一个GET方法,返回Response对象。

使用Rest Assured验证HTTP响应状态

每个客户端发送到服务器的HTTP请求都会收到一个HTTP响应,其中包含一个状态码。这个状态码告诉我们HTTP响应是否成功。其中我们进行了一个示例的REST API测试调用。接下来将讨论如何使用REST Assured来验证HTTP响应的状态。

一个HTTP响应对象通常代表了Web服务服务器发回的HTTP数据包(响应数据包),作为对客户端请求的响应。一个HTTP响应包含状态、header、body这几个部分。当我们说需要验证HTTP响应状态时,我们希望有一种机制来读取和验证整个响应对象,包括状态、header、body,需要验证HTTP响应的每个组成部分。

同一个REST API会以XML或JSON格式返回响应消息,格式取决于HTTP请求中的Media-Type属性。那么客户端如何知道它将从API获得什么类型的响应呢?这是由响应头来管理的。响应头包含一个Content-Type属性,用于通知响应主体格式的类型。

假设我们通过浏览器向图书商店发送GET请求,如下所示:

1
curl -X GET "https://demoqa.com/BookStore/v1/Books" -H "accept: application/json"

执行上述命令,结果如下:

如图,响应头中包含一个content-type属性,还有一些其他的属性值。通过解析这个头部,客户端就知道可以期待什么类型的响应(body)。

当客户端从服务器请求特定信息时,服务器会向客户端发送一个带有状态码的响应。服务器返回的状态码告诉我们请求是否成功。如果请求成功,服务器会在200-299的范围内发送状态码。如果请求未成功,则返回不在该范围内的状态码。

Rest Assured库提供了一个名为”io.restassured.response”的包,其中包含一个Response接口。Response接口提供了一些方法,可以帮助获取接收到的响应的各个部分。

其中方法 getStatusCode() 用于获取响应的状态码。该方法返回一个int,然后我们可以验证其值,这里使用TestNG Assert 用于验证状态码,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import static org.junit.Assert.*;
import org.testng.Assert; //用于验证响应状态
import org.testng.annotations.Test;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;

public class RestAssuredTestResponse {
@Test
public void GetBookDetails() {
// 指定 RESTful web 服务的基本 URL
RestAssured.baseURI = "https://demoqa.com/BookStore/v1/Books";
// 获取要发送到服务器的请求的 RequestSpecification
RequestSpecification httpRequest = RestAssured.given();

Response response = httpRequest.get("");

// 获取请求的状态码。
// 如果请求成功,状态码将是 200
int statusCode = response.getStatusCode();

// 断言返回正确的状态码。
Assert.assertEquals(statusCode /*实际值*/, 200 /*期望值*/, "返回了正确的状态码");
}
}

下面的代码用于获取状态码:

1
int statusCode = response.getStatusCode();

返回值 “statusCode” 被与期望值即 200 进行比较。如果两个值相等,则返回相应的消息或者打印正确的log。

1
2
// 断言正确的状态码已返回。
Assert.assertEquals(statusCode /*实际值*/, 200 /*期望值*/, "返回了正确的状态码");

通过这种方式,我们可以使用响应接口的 “getStatusCode()” 方法来验证响应的状态码,这是一种比较常见的场景,即接口返回成功的状态码2xx。接下来让我们讨论如何验证返回值不是 200(即错误状态码)的状态码,这也是另一种对接口的常见测试场景。

在实际的测试场景中可能存在诸如服务器宕机、REST API 不正常运行或请求本身存在问题等原因。总之,我们可能会遇到以下几种情况:

  1. 服务器宕机。
  2. 客户端请求不正确。
  3. 客户端请求的资源不存在。
  4. 在处理请求时服务器端发生错误。

当出现以上任何情况时,REST API 将返回一个错误状态码,客户端必须根据此状态码进行相应的处理。对之前给出的Demo链接,创建另一个测试,来模拟一个错误的场景。在这里,我们将验证当输入无效参数时, Web 服务返回的 HTTP 状态码。

我们提供参数以获取用户详细信息。这里我们提供了不存在的 userId 作为参数。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import static org.junit.Assert.*;
import org.testng.Assert; //用于验证响应状态
import org.testng.annotations.Test;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;

public class RestAssuredTestResponse {

@Test
public void GetPetDetails() {
// 指定 RESTful web 服务的基本 URL
RestAssured.baseURI = "https://demoqa.com/Account/v1/User/";
// 获取要发送到服务器的请求的 RequestSpecification
RequestSpecification httpRequest = RestAssured.given();

Response response = httpRequest.get("test");

// 获取请求的状态码。
// 如果请求失败,服务端返回的状态码将是401
int statusCode = response.getStatusCode();

// 断言正确的状态码已返回。
Assert.assertEquals(statusCode /*实际值*/, 401 /*期望值*/,
"返回了正确的状态码");
}
}

运行这个测试,因为返回的状态码和预期的期望是相同的,所以测试通过。

除了对状态码进行验证,我们还可以验证完整的状态行及其中包含的其他消息,”状态行” 是 HTTP 响应中返回的第一行,由三个子字符串组成:

  1. HTTP 协议版本。
  2. 状态码。
  3. 状态码的字符串值。

例如,当请求成功时,状态行将具有值 “HTTP/1.1 200 OK”。这里,第一部分是 HTTP 协议(HTTP/1.1)。接下来是 HTTP 状态码(200)。第三部分是状态消息(OK)。

我们可以使用响应接口的 getStatusLine() 方法来读取整个状态行。以下代码是一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void GetBookDetails() {
// 指定 RESTful web 服务的基本 URL
RestAssured.baseURI = "https://demoqa.com/BookStore/v1/Books";
// 获取要发送到服务器的请求的 RequestSpecification
RequestSpecification httpRequest = RestAssured.given();
Response response = httpRequest.get("");

// 将响应中的状态行读取到变量 statusLine 中
String statusLine = response.getStatusLine();
Assert.assertEquals(statusLine /*实际值*/, "HTTP/1.1 200 OK"
/*期望值*/, "返回了正确的状态码");
}

在这里,我们执行了类似于对状态码的测试。我们使用 getStatusLine() 方法读取状态行,并将其存储在字符串值中。然后,我们将此返回值与 “HTTP/1.1 200 OK” 进行比较,以检查状态是否成功。

对于header的检验大致和对于状态码的检验相同,不过header的检验更多用的是字符串相关的校验方法,此处不做详细说明。

对于body的检验比较复杂,后续根据实际场景再填充内容。

//TODO


如何使用Rest Assured进行RESTful API测试
https://douyushinyruo.github.io/shinyruotechtips/2024/02/bba85c2d0992/
作者
DouyuShinyruo
发布于
2024年2月20日
许可协议