接口请求方法
常见接口请求方法构造
- when().get(URL) 构造 HTTP 协议中的 GET 请求。
- when().post(URL) 构造 HTTP 协议中的 POST 请求。
- when().put(URL) 构造 HTTP 协议中的 PUT 请求。
- when().delete(URL) 构造 HTTP 协议中的 DELETE 请求。
RESTAssured的基本使用
- 准备数据:given()
- 发送请求:when()
- 验证返回结果:then()
接口请求参数
get 请求参数传递方式
- 方式一:直接拼接在 URL 中:
- ?username=Hogwarts&id=666
- 方式二:通过given()的queryParam()传递:
- given(): 表示发起请求之前的数据准备
- queryParam():单个URL参数
- queryParams():多个URL参数。
接口请求头
- HTTP 请求头是在 HTTP 请求消息中包含的元数据信息,用于描述请求或响应的一些属性和特征。
- 实际工作过程中具体要关注的头信息字段需要和研发沟通。
- 常见的头信息(右侧表格):
- Authorization: 表示客户端请求的身份验证信息
- Cookie: 表示客户端的状态信息,通常用于身份验证和会话管理
- Content-Type: 表示请求消息体的 MIME 类型
- User-Agent: 发送请求的客户端软件信息
- 其他: 根据接口文档设计要求决定其作用
Headers
- 简介
- HTTP Headers 也叫做 HTTP 消息头
- 允许客户端和服务器传递附加信息
- 由名称、冒号、具体的值组成
- 设置请求 Headers
- .header(): 添加单个 header 键值对,且支持多个值
- .headers(): 添加多个 header 键值对
- 示例源码
package com.vernon.headers_cookie;
import com.alibaba.fastjson2.JSONObject;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestHeadersCookie {
@Test
public void testHeaders() {
// 配置添加代理,方便 Charles 监听 请求和响应
RestAssured.proxy=host("127.0.0.1").withPort(8888);
// 忽略 HTTPS 校验
RestAssured.useRelaxedHTTPSValidation();
JSONObject headers = new JSONObject(){{
put("header1", "value1");
put("header2","value2");
}};
given()
// 添加单个 header 键值对,且支持多个值
.header("User-Agent", "Mozilla","Chrome")
// 添加多个 header 键值对
.headers(headers)
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.statusCode(200);
}
}
Cookie
- 设置 Cookie 的两种方式
- 通过header()/headers()添加
- 通过.cookie() / .cookies() 方法添加
- 添加cookie不能同时使用header 和cookie,同时存在时,通过cookie方法会覆盖header方法
- 示例源码
package com.vernon.headers_cookie;
import com.alibaba.fastjson2.JSONObject;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestHeadersCookie {
@Test
public void testCookies() {
// 配置添加代理,方便 Charles 监听 请求和响应
RestAssured.proxy=host("127.0.0.1").withPort(8888);
// 忽略 HTTPS 校验
RestAssured.useRelaxedHTTPSValidation();
given()
// 通过header/headers 添加 Cookie
.header("Cookie", "my_cookie1=value1")
// 通过cookie 添加 单个cookie信息
.cookie("my_cookie2","value2")
// 通过 cookies 添加多个 cookie信息
.cookies("1_cookie","1_value","2_cookie","2_value")
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.statusCode(200);
}
}
常用接口请求体应用场景
-
JSON(JavaScript Object Notation)
- 轻量级的数据交换格式,最常见的一种类型。
- Content-Type = application/json
-
表单数据(Form Data)
- 以键值对的形式提交数据,例如通过 HTML 表单提交数据。
- Content-Type = application/x-www-form-urlencoded
-
XML(eXtensible Markup Language)
- 常用的标记语言,
- 通常用于传递配置文件等数据。
- Content-Type = application/xml
- Content-Type = text/xml
-
文件(File)
- 可以通过请求体上传文件数据,例如上传图片、视频等文件。
- 上传文件的 MIME 类型
- Content-Type = image/jpeg
- Content-Type = multipart/form-data
-
纯文本(Text)
- 纯文本数据,例如发送邮件、发送短信等场景
- Content-Type = text/plain
-
其他格式
- 二进制数据、protobuf 等格式
接口请求体-json
JSON 类型接口请求体构造
- 通过given().body(jsonData)添加请求体信息:
- JSON 对象 。
- 直接复制 String 。
- JSONOject对象解析依赖
<!-- 阿里开源JSON解析包 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.52</version>
</dependency>
- 示例源码
package com.vernon.body.json;
import com.alibaba.fastjson2.JSONObject;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestJson {
@Test
void objectPostJson(){
// 直接构造一个 JSON 对象,适用于格式不复杂的场景
JSONObject requestBody = new JSONObject();
requestBody.put("key1", "value1");
requestBody.put("key2", "value2");
given()
// body()方法传递 JSON 请求体,类型为 String
.body(requestBody.toString()).log().all()
.when()
.post("https://httpbin.ceshiren.com/post")
// 打印全部的相应信息
.then().log().all();
}
@Test
void stringPostJson(){
// 从现有的请求信息中 copy 的请求体
String jsonData = "{\"username\":\"hogwarts\",\"password\":\"test12345\",\"code\":\"\"}";
given()
.body(jsonData).log().all()
.when()
.post("https://litemall.hogwarts.ceshiren.com/admin/auth/login")
// 打印全部的相应信息
.then().log().all();
}
}
接口请求体-文件
- Content-Type 类型: multipart/form-data
- REST-assured 上传文件
- 创建本地文件: hogwarts.txt
- 调用方法: multiPart()
- 参数:String name
- 参数:File file
- 示例源码
package com.vernon.multipart;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import java.io.File;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestMultipart {
@Test
void testMultipart(){
// 需要上传的文件对象
File file = new File("src/test/resources/hogwarts.txt");
// 定义本地代理配置
RestAssured.proxy = host("127.0.0.1").withPort(7897);
// 忽略 HTTPS 证书校验
RestAssured.useRelaxedHTTPSValidation();
given()
.log().all()
.contentType("multipart/form-data")
// 传递文件对象
.multiPart("hogwarts", file)
// 传递JSON数据
.multiPart("ceshiren","{\"hogwarts\",6666}","application/json")
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.log().all()
.statusCode(200);
}
}
接口请求体-form表单
- Content-Type 类型: application/x-www-form-urlencoded
- 应用场景
- 数据量不大
- 数据层级不深的情况
- 通常以键值对传递
- rest-assured 发送表单请求参数
- 单个参数传递: 调用 formParam() 方法
- 多个参数传递: 调用 formParams() 方法
- 示例源码
package com.vernon.form;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestForm {
@Test
public void testForm() {
RestAssured.proxy=host("127.0.0.1").withPort(7897);
RestAssured.useRelaxedHTTPSValidation();
HashMap<String, Object> form = new HashMap<>(){{
put("firstName", "John");
put("lastName", "Doe");
}};
given()
// 单个表单参数
.formParam("myParam", "myValue")
// 多个表单参数
.formParams(form)
.log().headers()
.log().body()
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.log().all()
.statusCode(200);
}
}
接口请求体-xml
- 示例接口地址:
http://dneonline.com/calculator.asmx
- xml文件读取解析IOUtils依赖
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> <scope>test</scope> </dependency>
- xml请求数据文件
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<Add xmlns="http://tempuri.org/">
<intA>1</intA>
<intB>1</intB>
</Add>
</Body>
</Envelope>
- 示例源码
package com.vernon.xml;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static io.restassured.RestAssured.given;
public class TestXml {
@Test
public void testXml() throws IOException {
// 定义请求体数据:源自 XML 文件对象
File file = new File("src/test/resources/add.xml");
FileInputStream fis = new FileInputStream(file);
String xmlStr = IOUtils.toString(fis, StandardCharsets.UTF_8);
fis.close();
given()
// 定制请求内容媒体类型
.contentType("text/xml; charset=utf-8")
// 定制请求体数据
.body(xmlStr)
.log().all()
.when()
.post("http://dneonline.com//calculator.asmx")
.then()
.log().all()
.statusCode(200);
}
}
接口响应断言
断言的目的
- 验证响应状态码。
- 验证响应体返回字段信息是否符合业务需求。
- 验证响应体字段的数据类型、数据格式。
响应状态码简介
响应状态码是 响应报文里的code字段
# 响应报文
< HTTP/1.1 200 OK
< Server: nginx/1.10.2
< Date: Thu, 12 Mar 2020 09:13:44 GMT
< Content-Type: image/png
< Content-Length: 11390
< Connection: keep-alive
响应断言的方式
- 状态码: then().statusCode()
- 响应头: then().header()
- 响应体: then().body()
响应状态码断言
- then().statusCode()
- 示例源码
package com.vernon.response.status;
import static io.restassured.RestAssured.given;
import org.junit.jupiter.api.Test;
public class TestAssertionStatusCode {
@Test
void testStatusCode(){
given()
.when()
.get("https://httpbin.ceshiren.com/get") // 发起GET请求
.then()
.log().all() // 打印响应结果
.statusCode(200); // 响应状态码断言
}
}
JSON响应体断言
- 方法1: 直接断言: then().body()。
- 结合 hamcrest 使用。
- body(“想要提取的信息”, 预期结果操作)。
- 使用 gpath 语法提取(了解)。
- 方法2: 提取后断言: then().extract().path();
- extract(): 提取方法,返回值为Response。
- path(): 从返回值中提取想要的信息(使用 gpath 语法)。
xml响应断言
- XPath 语法
/ 根节点
. 现行节点
// 不管位置,选择所有符合条件的元素
* 匹配所有元素节点
[] 迭代器标示
| 支持迭代器中做多选
- 示例代码
package com.vernon.xml;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
public class TestAssertionXML {
@Test
public void testAssertionXML() throws IOException {
// 定义请求体数据: 源自文件XML对象
File file = new File("src/test/resources/add.xml");
FileInputStream fis = new FileInputStream(file);
String xmlBody = IOUtils.toString(fis, StandardCharsets.UTF_8);
fis.close();
given()
.contentType("application/xml")
.body(xmlBody)
.when()
.post("http://dneonline.com//calculator.asmx")
.then()
.statusCode(200)
.body("//AddResult.text()",equalTo(2));
}
}
多层嵌套响应断言
- JSONPath 简介
- 在 JSON 数据中定位和提取特定信息的查询语言。
- JSONPath 使用类似于 XPath 的语法,使用路径表达式从 JSON 数据中选择和提取数据。
- 相比于传统的提取方式,更加灵活,并且支持定制化。
- JSONPath 语法
-
$.
查询的根节点对象,用于表示一个 json 数据,可以是数组或对象 -
@
过滤器(filter predicate)处理的当前节点对象 -
*
通配符 -
.
获取子节点 -
..
递归搜索,筛选所有符合条件的节点 -
?()
过滤器表达式,筛选操作 -
[start:end]
数组片段,区间为[start,end),左闭右开 -
[A]或[A,B]
迭代器下标,表示一个或多个数组下标
-
- JsonPath依赖
<!-- json path 解析json文件 -->
<properties>
<json-path.version>2.8.0</json-path.version>
</properties>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>${json-path.version}</version>
</dependency>
- 示例源码
public class TestJsonpath {
@Test
void jsonpathRes(){
String jsonData = "{\"username\":\"hogwarts\",\"password\":\"test12345\",\"code\":\"\"}";
String res = given()
.body(jsonData)
.when()
.post("https://httpbin.hogwarts.ceshiren.com/post")
.then()
.extract().response().getBody().asString();
// 第一种获取方式
DocumentContext context = JsonPath.parse(res);
ArrayList<String> codeList = context.read("$..code");
// 第二种获取方式
ArrayList<String> codeList2 = JsonPath.read(res, "$..code");
}
}
整体结构响应断言
- JSON Schema简介
- 是使用 JSON 格式编写的
- 可以用来定义校验 JSON 数据的结构
- 可以用来校验 JSON 数据的一致性
- 可以用来校验 API 接口请求和响应
- 生成JSON Schema文档
- 在线生成工具:
https://app.quicktype.io
- 复制 JSON 数据
- 粘贴到在线生成工具中
- 自动生成 JSON Schema 数据
- 在线生成工具:
- JSON Schema 依赖配置
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
- JSON Schema 响应断言
java语言示例
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
public class TestJsonSchema {
@Test
void func() {
given()
.header("Hello", "Hogwarts")
.when()
.get("https://httpbin.ceshiren.com/get") // 发送请求
.then()
.log().all() // 打印完整的响应信息
.assertThat()
.body(matchesJsonSchemaInClasspath("schema.json")); // JSON Schema 断言
}
}
python语言示例
import requests
from jsonschema import Draft7Validator, validates, RefResolver
# 定义Json Schema
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer", "minimum": 0}
},
"required": ["name", "age"]
}
# 获取API数据
response = requests.get('https://api.example.com/data')
# 确保请求成功
if response.status_code == 200:
data = response.json()
# 创建解决器和验证器
resolver = RefResolver('file://', schema)
validator = Draft7Validator(schema, resolver=resolver)
# 验证数据
if validator.is_valid(data):
print("数据验证通过")
else:
print("数据验证失败")
for error in validator.iter_errors(data):
print(error.message)
else:
print("请求API失败")
数据库操作与断言
数据库操作注意事项
- 直接对数据库做查询之外的操作是非常危险的行为
- 权限管理严格的公司数据库权限给的非常低
- 表结构复杂,随便删除数据会影响测试,甚至会导致系统出现异常
数据库封装
创建一个工具类,输入SQL,返回查询结果
添加依赖
<!-- jdbc 的driver依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 数据库查询结果集转换成标准Json格式的工具 -->
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.11.11</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.6.0</version>
<scope>test</scope>
</dependency>
- 代码示例
public class DatabaseUtils {
Statement statement;
Connection conn;
// 初始化数据库信息
public DatabaseUtils(String DB_URL, String USER, String PASS) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 问题: Operation not allowed for a result set of type ResultSet. TYPE_FORWARP_ONLY.
// 解决方案: createStatement 添加配置 ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY
statement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 根据 sql 返回在单个字段数据库查询的信息
public List<String> getResultStringListBySQL(String sql_query, String columnLabel){
List<String> resList = new ArrayList<>();
try {
ResultSet resultSet = statement.executeQuery(sql_query);
while (resultSet.next()){
resList.add(resultSet.getString(columnLabel));
}
}catch (SQLException e) {
e.printStackTrace();
}
return resList;
}
// 根据sql查询返回多个字段并转化为标准的json格式
public String getResultSetBySQL(String sql_query) {
// 格式转换将ResultSet 转换为 标准的json格式,方便断言时使用JsonPath获取对应信息。
try {
ResultSet resultSet = statement.executeQuery(sql_query);
// 获取所有
ResultSetMetaData md = resultSet.getMetaData();
int numCols = md.getColumnCount();
// 获取所有的数据表列名
List<String> colNames = new ArrayList<>();
for (int i = 1; i <= numCols; i++) {
String name = md.getColumnName(i);
colNames.add(name);
}
// 返回一个json结构的查询结果
return DSL.using(conn)
.fetch(resultSet)
.map(new RecordMapper() {
@Override
public JSONObject map(Record r) {
JSONObject obj = new JSONObject();
colNames.forEach(cn -> obj.put(cn, r.get(cn)));
return obj;
}
}).toString();
} catch (SQLException e) {
e.printStackTrace();
}
// 如果异常,return 空 json,不影响后续逻辑执行
return "{}";
}
}
接口超时处理
- 设置超时时间 步骤:
- 第1步: 创建 HttpClientConfig 实例对象,并设置响应超时时长为3秒,单位是毫秒
HttpClientConfig httpClientConfig = HttpClientConfig.httpClientConfig() .setParam("http.connection.timeout", 5000) .setParam("http.socket.timeout", 3000);
- 第2步: 创建 RestAssuredConfig 实例对象,传入 HttpClientConfig 实例对象
RestAssuredConfig restAssuredConfig = RestAssuredConfig.config() .httpClient(httpClientConfig);
- 第3步: given 语句中调用 config() 方法,传入 RestAssuredConfig 实例对象设置超时处理时间为3秒
given().config(restAssuredConfig)
- 第1步: 创建 HttpClientConfig 实例对象,并设置响应超时时长为3秒,单位是毫秒
- 示例源码
package com.vernon.timeout;
import io.restassured.RestAssured;
import io.restassured.config.HttpClientConfig;
import io.restassured.config.RestAssuredConfig;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestTimeout {
@BeforeAll
static void setupClass() {
RestAssured.baseURI = "https://httpbin.ceshiren.com";
// 配置全局代理
RestAssured.proxy = host("localhost").withPort(8888);
// 忽略 HTTPS 校验
RestAssured.useRelaxedHTTPSValidation();
}
@Test
void testCase1(){
given()
.when()
.get("/get")
.then()
.statusCode(200);
}
@Test
void testCase2(){
// 创建 HttpClientConfig 实例 并设置 超时时间为3秒
HttpClientConfig httpClientConfig = HttpClientConfig
.httpClientConfig()
.setParam("http.connection.timeout", 5000)
.setParam("http.socket.timeout", 3000);
// 创建 RestAssuredConfig 实例 并传入 HttpClientConfig 实例对象
RestAssuredConfig restAssuredConfig = RestAssuredConfig
.config()
.httpClient(httpClientConfig);
given()
// 传入 RestAssuredConfig 实例 设置超时时间为3秒
.config(restAssuredConfig)
// 配置局部代理
.proxy("localhost", 8888)
// 忽略 HTTPS 校验
.relaxedHTTPSValidation()
.log().all()
.when()
.get("/delay/10")
.then()
.statusCode(200);
}
@Test
void testCase3(){
given()
.when()
.get("/get")
.then()
.statusCode(200);
}
}
接口鉴权的多种情况与解决方案
- 接口鉴权就是: 身份认证
后端接口鉴权常用方法
cookie 鉴权
- cookie 的获取(根据接口文档获取)
- 发送携带 cookie 的请求
- 直接通过调用 cookie 方法
- 通过在 filter 中添加 cookieFilter 对象
- 示例源码
import io.restassured.filter.cookie.CookieFilter;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
// Cookie鉴权
public class AddCookieByFilter {
// 问题:通常 cookie 是不能像用户名密码一样有直接的数据的。
// 解决方案: 模拟正常的set-cookie的过程
@Test
void addCookieByFilter(){
CookieFilter cookieFilter = new CookieFilter();
// 第一次请求: 客户端向服务端发起 set-cookie 的请求操作
// -> https://httpbin.ceshiren.com/cookies/set/user/ad
// <- set-cookie
// -> redirect https://httpbin.ceshiren.com/cookies
given()
.redirects().follow(false).filter(cookieFilter)
.when()
.get("https://httpbin.ceshiren.com/cookies/set/user/ad")
.then()
.log().all();
// 问题: 如何在收到 set-cookie 后,将对应的 cookie 信息提取出来,并添加在下一次请求的 cookie 中。
// 解决方案: 使用 cookieFilter
// 第二次请求, 添加 cookie
given()
.filter(cookieFilter)
.when()
.get("https://httpbin.ceshiren.com/cookies")
.then()
.log().all();
// 第三次请求添加新的 cookie 字段值
given()
.filter(cookieFilter).cookie("counts","2")
.when()
.get("https://httpbin.ceshiren.com/cookies")
.then()
.log().all();
}
}
token 鉴权
- token 的获取(根据接口文档获取)
- 发送携带 token 的请求(根据接口文档获取)
- 示例源码
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
// token鉴权
public class AddToken {
//
@Test
void addToken() {
// token 的获取方法
String body = "{\"username\":\"admin123\",\"password\":\"admin123\",\"code\":\"\"}";
String token=given()
.contentType(ContentType.JSON)
.body(body)
.when()
.post("https://litemall.hogwarts.ceshiren.com/admin/auth/login")
.then()
.log().all()
.extract().path("data.token");
// 添加 token 以解决接口鉴权问题
// 强调: token 的认证过程,尽量与研发确认
given()
.header("X-Litemall-Admin-Token", token)
.when()
.get("https://litemall.hogwarts.ceshiren.com/admin/goods/list")
.then()
.log().all();
}
}
auth 鉴权
- 在基本 HTTP 身份验证中,请求包含格式为 的标头字段Authorization: Basic
- 其中credentials是 ID 和密码的Base64编码,由单个冒号连接:。
- 示例源码
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
// auth 鉴权
public class AddAuth {
@Test
void addAuth() {
given()
.auth().preemptive().basic("user", "ad")
.when()
.get("https://httpbin.ceshiren.com/basic-auth/user/ad")
.then()
.log().all();
}
}
代理配置
代理简介
- 介于客户端与服务器之间
- 可以监听请求和响应信息
- 充当防火墙和 Web 过滤器
代理与接口测试
- 更直观的排查请求错误
- 获取正确的的接口请求与响应信息
- 开启代理工具监听请求
- 获取自动化测试的请求与响应信息
- 对比两次请求响应的区别
REST assured 使用代理
- 开启代理工具监听请求
- 配置代理
- 局部:通过 proxy() 方法; 代理 HTTPS 请求,使用 useRelaxedHTTPSValidation() 忽略 HTTPS 证书验证
- 全局:定义 proxy 对象; 代理 HTTPS 请求,使用 relaxedHTTPSValidation() 忽略 HTTPS 证书验证
- 与
超时处理
同时使用时,必须配置局部代理
- 示例源码:
见超时代理
执行测试并生成allure报告
- mvn clean test -Dtest=com.vernon.petstore.QueryTest allure:report
- mvn allure:serve