BaseApi.java 封装类完整代码(含注释)
package com.test.day03;
import io.restassured.response.Response;
import java.util.Map;
import static io.restassured.RestAssured.given;
/**
* 接口请求封装类:支持 GET、POST、PUT、DELETE 请求
* 统一结构,便于代码复用与维护
*/
public class BaseApi {
/**
* 发起 HTTP 请求(支持 GET、POST、PUT、DELETE)
*
* @param method 请求方法,如 "get", "post", "put", "delete"
* @param url 接口地址
* @param headersMap 请求头,格式为 Map<String, Object>
* @param bodyData 请求体(用于 POST、PUT 请求),传 JSON 字符串
* @return Response 接口响应结果
*/
public Response request(String method, String url, Map<String, Object> headersMap, String bodyData) {
Response res = null;
if ("get".equalsIgnoreCase(method)) {
// 发起 GET 请求(无请求体)
res = given()
.headers(headersMap)
.when()
.get(url)
.then()
.log().all()
.extract().response();
} else if ("post".equalsIgnoreCase(method)) {
// 发起 POST 请求
res = given()
.headers(headersMap)
.body(bodyData)
.when()
.post(url)
.then()
.log().all()
.extract().response();
} else if ("put".equalsIgnoreCase(method)) {
// 发起 PUT 请求
res = given()
.headers(headersMap)
.body(bodyData)
.when()
.put(url)
.then()
.log().all()
.extract().response();
} else if ("delete".equalsIgnoreCase(method)) {
// 发起 DELETE 请求
res = given()
.headers(headersMap)
.body(bodyData)
.when()
.delete(url)
.then()
.log().all()
.extract().response();
}
return res;
}
}
BaseApi 接口请求封装类解释文档
一、类简介
类名: BaseApi
作用: 封装 RestAssured 接口请求逻辑,实现统一的 request() 方法,支持常用 HTTP 请求类型(GET、POST、PUT、DELETE),用于接口测试中简化重复操作。
二、方法功能说明
public Response request(String method, String url, Map<String, Object> headersMap, String bodyData)
| 参数 | 类型 | 说明 |
|---|---|---|
| method | String | 请求方法:get/post/put/delete(不区分大小写) |
| url | String | 请求地址 |
| headersMap | Map<String, Object> | 请求头,例如设置 Content-Type、token 等 |
| bodyData | String | 请求体(通常是 JSON 字符串),GET 请求可为 null |
| 返回值 | Response | RestAssured 的响应对象 |
三、代码结构解析与解释
1. 定义响应变量
Response res = null;
- 用于接收每种请求方式执行后的响应对象。
- 最终返回调用方,供提取字段、断言使用。
2. 判断请求类型(方法分发)
if ("get".equalsIgnoreCase(method)) {
// ...
} else if ("post".equalsIgnoreCase(method)) {
// ...
} else if ("put".equalsIgnoreCase(method)) {
// ...
} else if ("delete".equalsIgnoreCase(method)) {
// ...
}
equalsIgnoreCase()忽略大小写,更灵活。- 每种请求方式用独立分支执行,保持结构清晰。
- 方便后期单独扩展每种请求的附加功能。
3. GET 请求处理
res = given().headers(headersMap)
.when().get(url)
.then().log().all().extract().response();
headers(headersMap)设置请求头;.get(url)执行请求;.log().all()打印请求和响应日志(方便调试);.extract().response()提取原始响应。
逻辑原因: GET 请求不需要请求体,参数通常通过 URL 传递。
4. POST 请求处理
res = given().headers(headersMap).body(bodyData)
.when().post(url)
.then().log().all().extract().response();
- 除了请求头,额外设置了
.body(bodyData); - 常用于发送 JSON、form 表单等请求体内容。
逻辑原因: POST 请求是最常见的带参数提交方式,例如登录、添加商品等。
5. PUT / DELETE 请求处理
res = given().headers(headersMap).body(bodyData)
.when().put(url)
.then().log().all().extract().response();
- 同样支持传入请求体;
- DELETE 一般也可以带 body(虽然部分后端不支持)。
逻辑原因: 为了结构统一,所有支持请求体的方式都处理为 .body(...),调用者只需理解一套逻辑。
6. 返回响应对象
return res;
- 将请求响应返回给调用者;
- 调用者可进行字段提取、断言、打印原始 JSON、校验状态码等。
四、设计逻辑总结
| 设计目标 | 实现方式/原因 |
|---|---|
| 代码复用 | 所有请求方式集中一个方法处理,简化测试代码 |
| 灵活传参 | 通过 method/url/header/body 参数化,适配各种接口 |
| 易于调试 | 内置 .log().all() 输出所有请求/响应细节 |
| 接口一致性 | 所有请求方式统一返回 Response 对象,调用方式一致 |
| 可扩展性强 | 可在每个请求方式中加入签名处理、日志管理、重试机制等 |
五、后续扩展建议
你可以根据项目需要继续优化该类,例如:
-
支持 Query 参数 Map:
- 添加一个
Map<String, Object> queryParams参数 - 用
.queryParams(queryParams)动态构造 URL
- 添加一个
-
增加异常捕获:
- 用 try-catch 包裹请求逻辑,避免测试中断
- 可以在 catch 中打印错误日志或抛出 RuntimeException
-
支持默认请求头自动注入:
- 比如自动加上
"Content-Type"和"Authorization",不必每次手动设置
- 比如自动加上
-
封装断言辅助方法:
- 在该类中扩展如
assertStatusCode()、assertJsonKeyEquals()等常用断言
- 在该类中扩展如
完整的 AddCartTest.java(结构清晰 + 注释)
package com.test.day03;
import io.restassured.response.Response;
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.Map;
/**
* 用例说明:
* 登录商城系统 → 搜索商品 → 提取指定商品价格
* 作者:你自己
* 时间:2025年6月
*/
public class AddCartTest extends BaseApi {
@Test
public void add_one() {
// -------------------------------
// 步骤 1:用户登录,获取 token
// -------------------------------
String jsonData = "{\"principal\":\"lemon_auto\",\"credentials\":\"lemon123456\",\"appType\":3,\"loginType\":0}";
Map<String, Object> map = new HashMap<>();
map.put("Content-Type", "application/json; charset=UTF-8");
// 发起登录请求
Response response1 = request("post", "http://mall.lemonban.com:8107/login", map, jsonData);
// 提取 token 值
String token = response1.jsonPath().get("access_token");
String token_type = response1.jsonPath().get("token_type");
String tokenValue = token_type + token;
// 可选:将 token 加入后续请求头中(如果需要)
map.put("Authorization", tokenValue);
// -------------------------------
// 步骤 2:搜索商品,获取商品列表
// -------------------------------
// 发起商品搜索请求,关键词为“测试”
Response response2 = request("get", "http://mall.lemonban.com:8107/search/searchProdPage?prodName=测试", map, null);
// 可选:提取第一个商品的 prodId(动态方式)
// Object prodId = response2.jsonPath().get("records.prodId[0]");
// -------------------------------
// 步骤 3:根据指定 prodId 提取商品价格
// -------------------------------
// 使用固定 prodId(示例值为 5776)提取商品价格
Object price = response2.jsonPath().get("records.find{it.prodId==5776}.price");
System.out.println("商品 prodId=5776 的价格为:" + price);
}
}
补充说明(为什么这么写)
| 部分 | 写法理由 |
|---|---|
方法名 add_one | 适用于执行单个商品相关测试操作 |
| 分步骤注释 | 每一步明确标出功能,便于维护与教学 |
| map.put Authorization | token 复用写法,方便后续接口添加 |
GPath 表达式 .find{it.prodId==...} | 精确定位 JSON 数组内的某条记录 |
BaseApi 封装调用 request() | 保持测试方法简洁,不重复写 given-when-then |
当然可以,下面我将对你的 add_one() 方法进行更详细的描述,不只是解释代码“做了什么”,而是深入讲清楚每一部分的设计动机、接口背景、数据结构理解、为什么要这么处理。这会更贴近真实项目的思维流程,也更适合你写在技术笔记或测试文档中。
add_one() 方法描述、代码逻辑分析
方法目的说明
这个测试方法的核心目的是:
模拟一个用户登录商城系统后,搜索名为“测试”的商品列表,并从中查找出某个指定商品(prodId=5776)的价格。
这是接口自动化测试中典型的“前置数据准备 + 接口字段提取”场景。
代码逐段详细讲解
【1】构造登录数据并设置请求头
String jsonData = "{\"principal\":\"lemon_auto\",\"credentials\":\"lemon123456\",\"appType\":3,\"loginType\":0}";
Map<String, Object> map = new HashMap<>();
map.put("Content-Type", "application/json; charset=UTF-8");
解释:
-
jsonData是登录接口要求的请求体,结构是标准的 JSON 字符串,包含四个字段:principal:用户名credentials:密码appType:客户端类型(3 表示 h5,可能还有 APP、小程序等)loginType:登录方式(0 代表普通登录)
-
map是请求头,明确声明:本次请求体格式是 JSON。
为什么这么处理:
- 有些服务端要求
Content-Type是精确的"application/json; charset=UTF-8",否则返回 415; - 把请求头封装在
map中,便于传入后续统一请求方法中使用。
【2】登录并获取 token
Response response1 = request("post", "http://mall.lemonban.com:8107/login", map, jsonData);
String token = response1.jsonPath().get("access_token");
String token_type = response1.jsonPath().get("token_type");
String tokenValue = token_type + token;
map.put("Authorization", tokenValue);
解释:
-
使用
BaseApi中封装的request()方法发起 POST 请求登录; -
从返回的 JSON 中提取:
access_token:访问令牌,实际凭证token_type:令牌前缀(一般是 "Bearer ")
-
拼接出完整 token,格式为:
"Bearer xxxxx"; -
然后把这个 token 加入 map 中,后续接口可以直接用它完成鉴权。
为什么这么处理:
- 商城系统所有受保护接口都必须带 Authorization;
- 拼接后的 tokenValue 是标准格式,直接可用于请求头;
- 将其放入 map 后,后续请求都可复用,不用每次单独设置。
【3】搜索商品接口调用
Response response2 = request("get", "http://mall.lemonban.com:8107/search/searchProdPage?prodName=测试", map, null);
解释:
- 通过 GET 请求调用商品搜索接口;
prodName=测试表示搜索商品名称中包含“测试”的商品;- 将登录 token 作为 Authorization 请求头传入;
- 请求体为 null,因为 GET 请求不需要 body。
为什么这么处理:
- 为了动态获取商品数据,避免写死某个商品 ID;
- 这一步为“后续提取 price 或 prodId”等字段提供数据来源;
- 这个接口也是商城最常见的“用户行为入口”。
【4】提取指定商品的价格字段
Object price = response2.jsonPath().get("records.find{it.prodId==5776}.price");
System.out.println("price的打印结果为:" + price);
解释:
- 使用 RestAssured 提供的 GPath 表达式,对 JSON 中的数组进行过滤;
records.find{it.prodId==5776}表示从数组中找出第一个prodId=5776的商品;.price表示获取该商品的价格字段;- 最后打印出来,验证是否提取成功。
为什么这么处理:
records是接口返回的商品列表数组;- 如果你只知道目标商品 ID,但不知道它的位置,不能用索引,只能用
find{}来精准查找; - 这种写法兼容商品顺序变化,稳定性更高。
整体业务逻辑流程分析
- 系统要求鉴权,所以必须首先登录并获取 access_token。
- 商品相关接口都需要 token,所以要提前把 token 拼接并放入请求头中。
- 测试过程不适合写死商品数据,所以使用搜索接口动态拉取商品列表。
- 为了验证价格字段正确解析,使用 GPath 表达式精准定位商品并提取价格。
- 打印输出是为了测试验证是否提取正确、接口是否正常工作。
总结:方法最终完成了什么?
这个方法实现了以下目标:
| 步骤 | 功能 | 是否关键 |
|---|---|---|
| 1 | 登录系统,获取 token | 是 |
| 2 | 将 token 设置为请求头 Authorization | 是 |
| 3 | 搜索商品,获取商品数据 | 是 |
| 4 | 从结果中筛选出指定商品并提取价格 | 是 |
| 5 | 打印验证数据 | 辅助调试 |
416



被折叠的 条评论
为什么被折叠?



