【线上】接口自动化测试
接口自动化测试
预习准备
- 提前先预习完以下相关的知识,再开始本章节的学习。
- 提前注册企业微信账号。
专题课 | 阶段 | 章节 | Python 班级 | Java 班级 |
---|---|---|---|---|
接口自动化测试 | L1 | 接口自动化测试框架介绍 | 录播地址 | 录播地址 |
接口自动化测试 | L1 | 接口请求方法 | ||
接口自动化测试 | L1 | 接口请求参数 | ||
接口自动化测试 | L1 | 接口请求头 | ||
接口自动化测试 | L1 | 接口请求体-json | ||
接口自动化测试 | L1 | 接口响应断言 | ||
接口自动化测试 | L1 | json 响应体断言 | ||
接口自动化测试 | L2 | 多层嵌套响应断言 | ||
接口自动化测试 | L2 | 【实战】宠物商店接口自动化测试实战 | ||
接口自动化测试 | L3 | 整体结构响应断言 | ||
接口自动化测试 | L3 | 数据库操作与断言 | ||
接口自动化测试 | L4 | 多套被测环境 |
课程目标
- 掌握接口自动化测试用例设计方法。
- 掌握接口自动化测试中的各种格式的请求构造与响应断言技巧。
- 掌握接口自动化测试中复杂断言方法。
- L1:接口自动化测试用例设计
- L2:接口请求构造与响应断言
- L3:复杂断言与鉴权处理
- L4:加解密与多套被测环境
接口自动化测试价值与体系
知识点总结
点击查看:接口自动化测试知识点梳理.xmind
需求说明
- 企业微信
- 企业微信是腾讯微信团队打造的企业通讯与办公工具。
- 具有与微信一致的沟通体验,丰富的 OA 应用,和连接微信生态的能力。
- 可帮助企业连接内部、连接生态伙伴、连接消费者。专业协作、安全管理、人即服务。
- 完成企业微信部门管理接口自动化测试。
- 环境准备
- 企业微信注册。
- 企业微信白名单配置步骤:https://ceshiren.com/t/topic/22768
实战思路
需求分析
请求方式:GET/POST(HTTPS)
请求地址:https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
请求包体:
...
参数说明:
...
权限说明:
...
返回结果:
...
参数说明:
...
接口测试用例设计
编写接口自动化测试脚本
- 单接口自动化测试
- 接口业务流程自动化测试
单接口自动化测试
接口鉴权
接口鉴权是常见的一种安全机制,在本次实战中基本可以分为两部分。
- 验证把认证接口需要的数据放入请求体中。
- 认证后,验证成功获取到有效的 token 值。
后续的业务接口在请求后端服务时,都需要带上这个认证信息。
- 获取企业微信通讯录管理接口的 access_token
-
获取接口调用凭证-企业 ID
- Corpid(企业 ID)
- 每个企业都拥有唯一的 corpid
- 查看:管理后台【我的企业】--【企业信息】下查看【企业 ID】
-
获取接口调用凭证-Secret
- Secret(应用的凭证密钥)
- 通讯录管理 secret
- 查看:【管理工具】--通讯录同步
Python 代码实现
def test_get_token(self):
'''
获取 access_token
:return:
'''
# 定义凭证
corpid = "xxx"
corpsecret = "xxx"
# 方式一:
# url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpid}&corpsecret={corpsecret}"
# 方式二:
url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
# 定义请求参数
params = {
"corpid": corpid,
"corpsecret": corpsecret
}
# 发送 GET 请求
r = requests.get(url)
# 打印响应体
print(r.text)
Java 代码实现
@Test
void getToken() {
String corpid = "ww7f7d94d8b17ac268";
String secret = "yFxjxy0uHfAl9QIjjAgwTiMTdjq16z-K9eSRfNZvqV4";
Response response = given()
//获取请求的日志 参数信息 请求头 请求参数
.log().all()
.param("corpid", corpid)
.param("corpsecret", secret)
.when()
.get("https://qyapi.weixin.qq.com/cgi-bin/gettoken")
.then()
//获取响应日志 响应body 响应头信息
.log().all()
.extract().response();
log.info(response.path("access_token").toString());
}
部门创建单接口自动化测试
Python 代码实现
-
冒烟用例
def test_create_department(self): ''' 创建部门 :return: ''' url = f"{self.base_url}/cgi-bin/department/create?access_token={self.token}" # 定义请求体 data = { "name": "广州研发中心", "name_en": "RDGZ", "parentid": 1, "order": 1, "id": 210 } # 发送 post 请求 r = requests.post(url, json=data) print(r.text) # 断言接口状态 assert r.status_code == 200 # 断言接口响应体 assert r.json().get("errcode") == 0 # 断言部门创建的id ids = r.json().get('id') assert 210 == ids
-
参数化运行
@pytest.mark.parametrize( "name, name_en, parentid, order, depart_id, expect", [ ("", "JISHU2", 1, 2, 3, 40058), ("j", "JISHU2", 1, 3, 4, 0), ("技术部23", "JISHU23", 1, 30, 1, 60123) ] ) def test_create_department_by_params(self, name, name_en, parentid, order, depart_id, expect): ''' 参数化创建部门 ''' data = { "name": name, "name_en": name_en, "parentid": parentid, "order": order, "id": depart_id } url = f"{self.base_url}/cgi-bin/department/create?access_token={self.token}" # 发出 POST 请求 r = requests.post(url, json=data) print(r.text) # 断言接口状态 assert r.status_code == 200 # 断言接口响应 assert r.json().get("errcode") == expect
Java 代码实现
- 冒烟用例
@Test
void createDepartment() {
String reqJson = "{\n" +
"\"name\":\"" + "技术部4" + "\",\n" +
"\"name_en\":\"" + "JISHU4" + "\",\n" +
"\"parentid\":" + 1 + ",\n" +
"\"order\":" + 1 + ",\n" +
"\"id\":" + 5 + "\n" +
"}";
Response response = given()
.body(reqJson)
.contentType(ContentType.JSON.withCharset(StandardCharsets.UTF_8)) // 设置请求的内容类型为 JSON,并指定字符编码为 UTF-8
.log().all()
.when()
.post("https://qyapi.weixin.qq.com/cgi-bin/department/create?access_token=" + getToken())
.then()
.log().all()
.statusCode(200) //断言状态码
.body("errcode", is(0)).extract().response();
Integer errcode = JsonPath.read(response.getBody().asString(), "$.errcode");
}
- 参数化运行
@ParameterizedTest
@CsvSource({
"技术部1,jishu1,1,1,2",
"技术部2,jishu2,1,1,3",
"技术部3,jishu3,1,1,4",
})
void createDepartmentParams(String name, String name_en, Integer parentid, Integer order, Integer id) {
String reqJson = "{\n" +
"\"name\":\"" + name + "\",\n" +
"\"name_en\":\"" + name_en + "\",\n" +
"\"parentid\":" + parentid + ",\n" +
"\"order\":" + order + ",\n" +
"\"id\":" + id + "\n" +
"}";
Response response = given()
.body(reqJson)
.contentType(ContentType.JSON.withCharset(StandardCharsets.UTF_8)) // 设置请求的内容类型为 JSON,并指定字符编码为 UTF-8
.log().all()
.when()
.post("https://qyapi.weixin.qq.com/cgi-bin/department/create?access_token=" + getToken())
.then()
.log().all()
.statusCode(200) //断言状态码
.body("errcode", is(0))
.extract().response();
}
接口业务流程测试
测试用例
测试模块 | 用例标题 | 前置条件 | 用例步骤 | 预期结果 | 实际结果 |
---|---|---|---|---|---|
部门模块 | 创建重复部门 | 1. 登录成功2. 已存在要添加的部门信息 | 1. 有存在部门的情况下,创建相同信息的部门失败2. 删除部门3. 查询部门删除成功4. 再次创建部门5. 查询部门创建成功 | 1.创建已有部门失败2.删除后创建成功 |
重复创建部门流程测试
Python 实现
def test_create_department_exist(self):
'''
创建部门前,部门已存在,先删除,然后创建
省略断言
'''
# 1. 准备已存在部门
depart_id = 220
create_data = {
"name": "广州研发中心",
"name_en": "RDGZ",
"parentid": 1,
"order": 1,
"id": depart_id
}
r = requests.request("POST", self.create_url, json=create_data)
# 准备已存在部门结果为 r.text
# 2. 有存在部门的情况下,创建相同信息的部门失败
r = requests.request("POST", self.create_url, json=create_data)
# 3. 删除部门-->删除部门结果为 r.text
params = {
"id": depart_id
}
r = requests.request("GET", self.delete_url, params=params)
# 4. 查询部门删除成功-->查询部门结果为r.text-->获取 id 列表
r = requests.request("GET", self.list_url)
id_list = [i['id'] for i in r.json().get("department_id")]
# 5. 再次创建部门-->再次创建部门结果为 r.text
r = requests.request("POST", self.create_url, json=create_data)
# 6. 查询部门创建成功-->查询部门结果为 r.text-->获取 id 列表
r = requests.request("GET", self.list_url)
id_list = [i['id'] for i in r.json().get("department_id")]
Java 版本
@Test
void createDepartmentExist(){
//省略具体代码实现
// 1. 准备已存在部门
···
// 2. 有存在部门的情况下,创建相同信息的部门失败
···
// 3. 删除部门
···
// 4. 查询部门删除成功
···
// 5. 再次创建部门
···
// 6. 查询部门创建成功
···
}
接口测试框架封装
框架设计思路
项目结构
├── apis
│ ├── base_api
│ ├── department
│ └── wework
├── config
│ └── config.yaml
├── tests
│ ├── test_departments
│ └── test_department_flow
└── utils
├── log_utils
└── utils
- ApiObject 设计模式:
- 封装。
- 分层。
- 把实现和测试用例以及断言进行拆分。
分层 | 作用 | 示例 |
---|---|---|
base_api | 封装和接口框架直接相关的方法 | 接口请求封装 |
wework | 封装业务的全局操作 | 获取 token获取配置数据 |
department | 封装子模块的操作 | 部门信息的增删改查 |
test_department | 测试步骤、业务流程及断言 | 添加部门用例 |
框架搭建
获取 access_token
企业微信特有逻辑,完成 access_token 的获取
- Python 版本
class Wework:
def __init__(self):
self.base_url = "https://qyapi.weixin.qq.com"
self.token = self.get_access_token()
def get_access_token(self):
'''
获取 access_token
:return:
'''
# 定义凭证
corpid = "xxx"
corpsecret = "xxx"
url = f"{self.base_url}/cgi-bin/gettoken"
# 定义请求参数
params = {
"corpid": corpid,
"corpsecret": corpsecret
}
# 发送 GET 请求
r = requests.get(url, params=params)
# 打印响应体
print(r.text)
# 获取 access_token
token = r.json().get("access_token")
return token
- Java 版本
@Slf4j
public class Wework {
String CORPID;
ConfigEntity config;
public Wework() {
// 读取配置
config = ConfigUtil.getConfig();
CORPID = config.getCorpId();
RestAssured.baseURI = config.getBaseUrl();
}
String getToken(String sercret) {
Response response = given()
//获取请求的日志 参数信息 请求头 请求参数
.param("corpid", CORPID)
.param("corpsecret", sercret)
.when()
.get("/gettoken")
.then()
//获取响应日志 响应body 响应头信息
.extract().response();
return response.path("access_token").toString();
}
}
封装对于业务模型
接口信息描述:只关注业务,不需要做断言。每个方法返回接口响应体。
- python 版本
# 部门需要继承 Wework,这样就可以直接获取 Wework 中获取到的 access_token
class Department(Wework):
def create(self, data):
'''
创建部门
:return:
'''
create_url = f"{self.base_url}/cgi-bin/department/create?access_token={self.token}"
r = requests.request(method="POST", url=create_url, json=data)
return r
...
- java 版本
public class WeworkDepartment extends Wework {
String SECRET;
public WeworkDepartment() {
// 读取配置
super();
SECRET = config.getContactsSecret();
}
/**
* 部门创建
*
* @param dce 创建部门数据实体类
* @return
*/
public Response create(DepartmentCreateEntity dce) {
···
}
/**
* 部门删除
* @param depart_id 部门id
*/
public void delete(Integer depart_id) {
···
}
/**
* 获取部门id列表
* @return
*/
public List<Integer> getDepartmentIds() {
···
}
}
自动化测试用例实现
- Python 版本
class TestDepartments:
def setup_class(self):
# 实例化部门类
self.department = Department()
# 准备测试数据
self.depart_id = 220
self.create_data = {
"name": "广州研发中心",
"name_en": "RDGZ",
"parentid": 1,
"order": 1,
"id": self.depart_id
}
self.update_name = "广州研发中心-update"
self.update_data = {
"id": self.depart_id,
"name": self.update_name
}
def test_department_flow(self):
'''
部门增删改查场景测试
'''
# 创建部门
r = self.department.create(self.create_data)
# 查询是否创建成功
r = self.department.get()
depart_ids = [i['id'] for i in r.json().get('department_id')]
assert self.depart_id in depart_ids
# 更新部门信息
r = self.department.update(self.update_data)
# 删除部门
r = self.department.delete(self.depart_id)
# 查询是否删除成功
r = self.department.get()
depart_ids = [i['id'] for i in r.json().get('department_id')]
assert self.depart_id not in depart_ids
- Java 版本
public class DepartmentApiObjectsTest {
static WeworkDepartment weworkDepartment;
@BeforeAll
static void setUp() {
weworkDepartment = new WeworkDepartment();
}
@Test
void createDepartmentExist() {
// 1. 准备已存在部门
DepartmentCreateEntity createDepartment = new DepartmentCreateEntity("技术部", "JISHU1", 1, 1, 2);
weworkDepartment.create(createDepartment);
// 2. 有存在部门的情况下,创建相同信息的部门失败
Response response = weworkDepartment.create(createDepartment);
assertThat(response.path("errcode"), is(60008));
// 3. 删除部门
weworkDepartment.delete(createDepartment.getId());
// 4. 查询部门删除成功
List<Integer> ids = weworkDepartment.getDepartmentIds();
assertThat(ids, not(hasItem(createDepartment.getId())));
// 5. 再次创建部门
weworkDepartment.create(createDepartment);
// 6. 查询部门创建成功
ids = weworkDepartment.getDepartmentIds();
assertThat(ids, hasItem(createDepartment.getId()));
}
}
框架优化
封装工具类
- 创建 test_env.yaml,文件中配置和业务强相关的数据。
- 创建工具类。
- 修改 wework 中配置数据获取方式。
Python 实现
- base_api.py 文件
def send_api(self,req):
'''
对 request 完成二次封装
:param req:
:return:
'''
...
- test.yaml 文件
# 接口自动化测试的基础服务地址抽离
base_url: https://xxx
# 和账户相关的信息
corpid:
hogwarts: xxx
corp_secret:
contacts: xxx
- wework.py 文件
class Wework(BaseApi):
def __init__(self):
# 获取配置数据
...
def get_token(self, secret):
'''
获取 token
:return:
'''
...
def get_config(self):
'''
获取配置数据
:return:
'''
...
- departments.py 文件
class Departments(Wework):
def __init__(self):
super().__init__()
# 获取通讯录的secret
...
def create(self, data):
'''
创建部门接口
:param data: json 格式请求体
:return:
'''
...
多环境切换
Python 代码实现
class Wework(BaseApi):
def __init__(self):
...
def get_access_token(self):
....
def get_config(self):
'''
获取配置数据
:return:
'''
# 获取对于环境文件路径
env = os.getenv('env', default='test')
path = f"{Utils.get_root_path()}/config/{env}_env.yaml"
yaml_data = Utils.get_yaml_data(path)
# 获取需要的值
...
Java 代码实现
- config.yaml文件
baseUrl: "https://qyapi.weixin.qq.com/cgi-bin"
corpId: "ww7f7d94d8b17ac268"
contactsSecret: "yFxjxy0uHfAl9QIjjAgwTiMTdjq16z-K9eSRfNZvqV4"
- 配置实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ConfigEntity {
private String baseUrl;
private String corpId;
private String contactsSecret;
}
- 配置文件读取
public class ConfigUtil {
/**
* 获取项目配置文件
*
* @return 配置实体类
*/
public static ConfigEntity getConfig() {
try {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
ConfigEntity configEntity = mapper.readValue(ConfigEntity.class.getClassLoader().getResourceAsStream("config.yaml"), ConfigEntity.class);
return configEntity;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
数据库配置
响应断言
课后练习
- 完成接口自动化测试框架搭建
- 添加获取部门成员 ID的场景
- 添加复杂断言
- 使用 JSONPath 断言
- 使用 JSONSchema 断言
- 添加复杂断言
- 添加多环境配置
- 添加数据清理步骤
- 添加日志
- 添加测试报告
- 数据库断言
总结
本次课程重点学习接口自动化测试的内容,包括单接口测试和业务流程测试脚本的编写,同时涉及搭建接口自动化测试框架及优化辅助测试。