02. 接口自动化测试框架开发
2024年10月28日大约 16 分钟
02. 接口自动化测试框架开发
1. 接口自动化框架设计思路
- 搭建基础框架
- 定义项目目录结构,安装依赖包
- 通用功能类封装
- 数据库工具类……
- 用例基础代码
- 封装接口API对象+UnitTest框架编写测试脚本
- 测试数据参数化
- 测试数据json文件设计、参数化实现
- 用例组织运行
- 组织测试用例运行,生成测试报告
1. 定义项目目录结构
安装依赖包
安装requests包
安装parameterized包
安装PyMySQL包
安装htmlTestReport包
# requires.txt
HTMLTestReport==1.0.1
parameterized==0.8.1
PyMySQL==1.0.2
requests==2.28.1
2. 封装数据库操作工具类
提示:为减少代码冗余,提高测试效率,将数据库相 操作封装成工具类
数据库工具类要求:
实现的功能:
1. 获取数据库连接对象方法
get_conn()
2. 关闭数据库连接对象
close_conn()
3. 查询一条记录
get_one()
4. 更新数据库
uid_db()
3. API对象封装+测试脚本
核心实现:代码分层思想
解决问题:
- 代码冗余代码
- 耦合度高
测试脚本层:
- 关注测试数据准备和断言
- 关注业务流程的处理
接口对象层:
- 关注接口参数细节
- 返回响应结果给脚本层
使用UnitTest管理测试脚本:
测试脚本层:
- 登录模块:test_login.py
- 员工模块:test_employee.py
- 实现要求:
- 完成测试脚本的业务实现
- 通过fixture进行测试数据构造和清理
接口对象层
- 按功能模块封装接口对象
- 登录模块:login.py
- 员工模块:employee.py
- 实现要求:
- API对象封装,使用通用参数
- 抽离管理域名等共性信息
4. 测试数据参数化-数据组织+参数化
数据组织:
- 数据文件:JSON文件
- 数据形式:列表嵌套字典 [{},{},......]
参数化:
- 封装读取json文件,构造参数化数据的测试方法
- 测试用例中使用 parameterized实现参数化
5. 生成测试报告
2. 接口自动化测试框架开发实现
代码路径:test_code\ae_apiAutoTestFrameWork_a\apitest_ihrm_a
1. 封装iHRM登录
1. 登录接口
1. 普通方式实现
import unittest
import requests
class TestIhrmLogin(unittest.TestCase):
# 测试方法1,登录成功
def test01_login_success(self):
# 组织url
url = "http://192.168.10.31:58100/sys/login"
header = {"Content-Type": "application/json"}
json_data = {"mobile": "13800000002", "password": "123456"}
resp = requests.post(url=url, headers=header, json=json_data)
print("登录成功:", resp.json())
# 断言
self.assertEqual(200, resp.status_code)
self.assertEqual(True, resp.json().get("success"))
self.assertEqual(10000, resp.json().get("code"))
self.assertIn("操作成功", resp.json().get("message"))
# 测试方法2,密码错误
def test02_pwd_err(self):
# 组织url
url = "http://192.168.10.31:58100/sys/login"
header = {"Content-Type": "application/json"}
json_data = {"mobile": "13800000002", "password": "123456789"}
resp = requests.post(url=url, headers=header, json=json_data)
print("密码错误:", resp.json())
# 断言
self.assertEqual(200, resp.status_code)
self.assertEqual(False, resp.json().get("success"))
self.assertEqual(20001, resp.json().get("code"))
self.assertIn("用户名或密码错误", resp.json().get("message"))
2. 登录接口对象层
1. 在 api/ 下,创建 ihrm_login_api.py 文件。
2. 在文件内,封装 IhrmLoginApi 类,添加 login 类方法。
3. 按照 普通方式实现,分析。实现 login 类方法。
# config.py
# config.py
"""存放 全局变量"""
import os
import sys
# 全局手机号
TEL = "18000000000"
TOKEN = ""
# 接口基础路径
BASE_URL = "http://192.168.10.31:58100"
# BASE_URL = "http://ihrm-java.ithiema.net"
# 项目根路径
BASE_PATH = os.path.dirname(__file__)
# 加入到查询路径
sys.path.append(BASE_PATH)
DB_CONFIG = {
"host": "192.168.10.31",
"port": 3306,
"user": "root",
"password": "admin",
"database": "ihrm",
"charset": "utf8"
}
if __name__ == '__main__':
print(BASE_PATH)
print(sys.path)
# api/ihrm_login.py
import requests
from config import BASE_URL
class IhrmLoginApi(object):
# 登录方法
@classmethod
def login(cls, json_data):
url = BASE_URL + "/sys/login"
header = {"Content-Type": "application/json"}
resp = requests.post(url=url, headers=header, json=json_data)
print(
f'request:\n\turl:{url}\n\tmethod:{resp.request.method}\n\theader:{header}\n\tdata:{json_data}\nresponse:\n\t{resp.json()}')
return resp
if __name__ == '__main__':
data = {"mobile": "13800000002", "password": "123456"}
resp = IhrmLoginApi.login(data)
3. 登录接口测试用例层
1. 在 scripts/ 下,创建 test_ihrm_login.py 文件
2. 在 文件内,创建 测试类 TestIhrmLogin 从 unittest.TestCase 继承
3. 添加 测试方法, 并实现
# script/test_ihrm_login.py
import unittest
from api.ihrm_login import IhrmLoginApi
from common.assert_util import assert_util
class TestIhrmLogin(unittest.TestCase):
"""登录"""
def test01_login_success(self):
self._testMethodName = "登录成功"
self._testMethodDoc = ""
# 组织请求数据
json_data = {"mobile": "13800000002", "password": "123456"}
# 调用自己封装的接口
resp = IhrmLoginApi.login(json_data)
print("登录成功:", resp.json())
# 断言
assert_util(self, resp, 200, True, 10000, "操作成功")
def test02_mobile_none(self):
self._testMethodName = "手机号为空"
self._testMethodDoc = ""
# 组织请求数据
json_data = {"mobile": None, "password": "123456"}
# 调用自己封装的接口
resp = IhrmLoginApi.login(json_data)
print("手机号为空:", resp.json())
# 断言
assert_util(self, resp, 200, False, 20001, "用户名或密码错误")
#
def test03_pwd_err(self):
self._testMethodName = "密码错误"
self._testMethodDoc = ""
# 组织请求数据
json_data = {"mobile": "13800000002", "password": "123456890"}
# 调用自己封装的接口
resp = IhrmLoginApi.login(json_data)
print("密码错误:", resp.json())
# 断言
assert_util(self, resp, 200, False, 20001, "用户名或密码错误")
4. 封装断言方法
1. 在 common/ 下,新建文件 assert_util.py 文件,
2. 在文件内,添加 函数 assert_util()
3. 在 函数内,实现通用的断言函数。
4. 在 测试方法中,使用 直接封装的 通用断言函数, 实现断言
# 定义 通用断言方法
def assert_util(self, resp, status_code, success, code, message):
"""
通用断言方法
:param self: UnitTest子类
:param resp: 响应数据
:param status_code: 响应状态码
:param success: 成功状态
:param code: 响应码
:param message: 响应消息
:return:
"""
self.assertEqual(status_code, resp.status_code)
self.assertEqual(success, resp.json().get("success"))
self.assertEqual(code, resp.json().get("code"))
self.assertIn(message, resp.json().get("message"))
- 使用断言方法
assert_util(self, resp, 200, True, 10000, "操作成功")
assert_util(self, resp, 200, False, 20001, "用户名或密码错误")
assert_util(self, resp, 200, False, 20001, "用户名或密码错误")
2. 参数化
- 参数化的核心
- 数据驱动(用数据驱动测试用例执行)
- 数据驱动
- 针对一个接口,只写一个测试方法。用一份测试数据文件,管理各个测试用例的测试数据。
1. 回顾UnitTest参数化
原始案例
import unittest # 待测试方法 def add(x, y): return x + y class TestAdd(unittest.TestCase): def test01_add(self): res = add(10, 20) self.assertEqual(30, res) def test02_add(self): res = add(100, 200) self.assertEqual(300, res) def test03_add(self): res = add(1000, 20) self.assertEqual(1020, res)
参数化实现
import json import unittest from parameterized import parameterized # 待测试方法 def add(x, y): return x + y # 测试数据 data = [ {"x": 10, "y": 20, "except": 30}, {"x": 100, "y": 200, "except": 300}, {"x": 1000, "y": 20, "except": 1020}, {"x": 4, "y": 18, "except": 23} ] # 读取数据 def read_json_data(): list_data = [] for item in data: tmp = tuple(item.values()) list_data.append(tmp) return list_data # 文件读取 # [{},{},{}] ---> [(),(),()] | [[],[],[]] def read_json_data(): list_data = [] # 从 .json 文件中,读取 [{},{},{}] 数据 with open("./02-params_data.json", "r", encoding="utf-8") as f: data = json.load(f) for item in data: tmp = list(item.values()) list_data.append(tmp) return list_data """ 参数实现步骤: 1. 导包 from parameterized import parameterized 2. 在通用测试方法上一行,添加 @parameterized.expand() 3. 给 expand() 传入 [(),(),()] 格式数据。( 调用 read_json_data() ) 4. 修改 通用测试方法形参,按 数据中的 key 设计 参数。 5. 在 通用测试方法 内,使用形参 6. 运行类 """ class TestAdd(unittest.TestCase): # 通用测试方法(实现参数化) @parameterized.expand(read_json_data()) def test_add(self, x, y, except_data): res = add(x, y) self.assertEqual(except_data, res) # def test01_add(self): # res = add(10, 20) # self.assertEqual(30, res) # # def test02_add(self): # res = add(100, 200) # self.assertEqual(300, res) # # def test03_add(self): # res = add(1000, 20) # self.assertEqual(1020, res) if __name__ == '__main__': unittest.main()
从json文件读取
- 创建 json 文件: params_data.json,写入 [{},{},{}] 格式数据
data = [ {"x": 10, "y": 20, "except": 30}, {"x": 100, "y": 200, "except": 300}, {"x": 1000, "y": 20, "except": 1020}, {"x": 4, "y": 18, "except": 23} ]
- 修改 读取数据的read_json_data 函数,添加打开json文件,读数据的代码
# [{},{},{}] ---> [(),(),()] | [[],[],[]] def read_json_data(): list_data = [] # 从 .json 文件中,读取 [{},{},{}] 数据 with open("./params_data.json", "r", encoding="utf-8") as f: data = json.load(f) for item in data: tmp = tuple(item.values()) list_data.append(tmp) return list_data
2. 登陆接口参数化
准备数据 data/ihrm_login.json
[ { "desc": "登录成功", "req_data": { "mobile": "13800000002", "password": "123456" }, "stauts_code": 200, "success": true, "code": 10000, "message": "操作成功" }, { "desc": "手机号为空", "req_data": { "mobile": null, "password": "123456" }, "stauts_code": 200, "success": false, "code": 20001, "message": "用户名或密码错误" }, { "desc": "密码错误", "req_data": { "mobile": "13800000002", "password": "123456789" }, "stauts_code": 200, "success": false, "code": 20001, "message": "用户名或密码错误" }, { "desc": "多参", "req_data": { "mobile": "13800000002", "password": "123456", "abc": "123" }, "stauts_code": 200, "success": true, "code": 10000, "message": "操作成功" }, { "desc": "少参", "req_data": { "password": "123456" }, "stauts_code": 200, "success": false, "code": 20001, "message": "用户名或密码错误" }, { "desc": "无参", "req_data": null, "stauts_code": 200, "success": false, "code": 99999, "message": "抱歉,系统繁忙,请稍后重试!" }, { "desc": "错误参数", "req_data": { "abc": "13800000002", "password": "123456" }, "stauts_code": 200, "success": false, "code": 20001, "message": "用户名或密码错误" } ]
读取数据文件
1. 在 common/ 下 创建 read_json_util.py 文件 2. 在 文件内,定义函数,从 json文件中读取数据,转换成 元组列表,返回
import json # 定义函数,读取 data/xxx.json 文件 def read_json_data(): with open("../data/add_emp.json", "r", encoding="utf-8") as f: json_data = json.load(f) list_data = [] for item in json_data: # tmp = tuple(item.values()) tmp = list(item.values()) list_data.append(tmp) # 返回,坚决不能在 for 内 return list_data if __name__ == '__main__': ret = read_json_data() print(ret)
使用 parameterized 实现参数化
步骤: 1. 导包 from parameterized import parameterized 2. 在 通用测试方法上一行,添加 @parameterized.expand() 3. 给 expand() 传入 元组列表数据( 调用 自己封装的 读取 json 文件的 函数 read_json_data() ) 4. 修改 通用测试方法形参,与 json 数据文件中的 key 一致。 5. 在 通用测试方法内,使用形参
import json import unittest from parameterized import parameterized from api.ihrm_login import IhrmLoginApi from common.assert_util import assert_util from common.read_json_util import read_json_data """ 1. 导包 from parameterized import parameterized 2. 在 通用测试方法上一行,添加 @parameterized.expand() 3. 给 expand() 传入 元组列表数据( 调用 自己封装的 读取 json 文件的 函数 read_json_data() ) 4. 修改 通用测试方法形参,与 json 数据文件中的 key 一致。 5. 在 通用测试方法内,使用形参 """ class TestIhrmLoginParams(unittest.TestCase): """登录测试(参数化)""" # 通用测试方法(实现参数化) @parameterized.expand(read_json_data()) def test_login(self, desc, req_data, stauts_code, success, code, message): self._testMethodName = desc self._testMethodDoc = f'[{json.dumps(req_data)}]' # 调用自己封装的接口 resp = IhrmLoginApi.login(req_data) print(desc, ":", resp.json()) # 断言 assert_util(self, resp, stauts_code, success, code, message)
2. 员工管理接口
1. 普通方法实现
import requests
# 添加员工
url = "http://192.168.10.31:58100/sys/user"
header = {"Content-Type": "application/json", "Authorization": "Bearer a94e651d-cadb-4d27-9944-4df7e2ffa4ad"}
json_data = {
"username": "业务猪001",
"mobile": "13978734783",
"workNumber": "9527"
}
resp = requests.post(url=url, headers=header, json=json_data)
print("添加员工:", resp.json())
# 查询员工
url_query = "http://192.168.10.31:58100/sys/user/1469566449784717312"
header_query = {
"Content-Type": "application/json",
"Authorization": "Bearer a94e651d-cadb-4d27-9944-4df7e2ffa4ad"
}
resp_query = requests.get(url=url_query, headers=header_query)
print("查询员工:", resp_query.json())
# 修改员工
url_modify = "http://192.168.10.31:58100/sys/user/1469566449784717312"
header_modify = {
"Content-Type": "application/json",
"Authorization": "Bearer a94e651d-cadb-4d27-9944-4df7e2ffa4ad"
}
modify_data = {"username": "齐天大圣"}
resp_modify = requests.put(url=url_modify, headers=header_modify, json=modify_data)
print("修改员工:", resp_modify.json())
# 删除员工
url_del = "http://192.168.10.31:58100/sys/user/1469566449784717312"
header_del = {
"Content-Type": "application/json",
"Authorization": "Bearer a94e651d-cadb-4d27-9944-4df7e2ffa4ad"
}
resp_del = requests.delete(url=url_del, headers=header_del)
print("删除员工:", resp_del.json())
2. 接口对象层
# api/ihrm_tmp_crud.py
"""
员工管理模块的 接口对象层
"""
import requests
from config import BASE_URL
class IhrmEmpCURDAPI:
"""员工管理(crud)测试"""
# 添加员工
@classmethod
def add_emp(cls, header, json_data):
url = BASE_URL + "/sys/user"
resp = requests.post(url=url, headers=header, json=json_data)
print(
f'request:\n\turl:{url}\n\tmethod:{resp.request.method}\n\theader:{header}\n\tdata:{json_data}\nresponse:\n\t{resp.json()}')
return resp
# 查询员工
@classmethod
def query_emp(cls, emp_id, header):
url = BASE_URL + "/sys/user/" + emp_id
resp = requests.get(url=url, headers=header)
print(
f'request:\n\turl:{url}\n\tmethod:{resp.request.method}\n\theader:{header}\n\tdata:{None}\nresponse:\n\t{resp.json()}')
return resp
# 修改员工
@classmethod
def modify_emp(cls, emp_id, header, modify_data):
url = BASE_URL + "/sys/user/" + emp_id
resp = requests.put(url=url, headers=header, json=modify_data)
print(
f'request:\n\turl:{url}\n\tmethod:{resp.request.method}\n\theader:{header}\n\tdata:{modify_data}\nresponse:\n\t{resp.json()}')
return resp
# 删除员工
@classmethod
def delete_emp(cls, emp_id, header):
url = BASE_URL + "/sys/user/" + emp_id
resp = requests.delete(url=url, headers=header)
print(
f'request:\n\turl:{url}\n\tmethod:{resp.request.method}\n\theader:{header}\n\tdata:{None}\nresponse:\n\t{resp.json()}')
return resp
if __name__ == '__main__':
header = {"Content-Type": "application/json", "Authorization": "Bearer b040daed-39c1-4302-8777-f950770c8a26"}
data_add = {
"username": "业务猪001",
"mobile": "13978734786",
"workNumber": "9527"
}
resp = IhrmEmpCURDAPI.add_emp(header, data_add)
emp_id = "1469572901224054784"
resp = IhrmEmpCURDAPI.query_emp(emp_id, header)
data = {"username": "齐天大圣"}
resp = IhrmEmpCURDAPI.modify_emp(emp_id, header, data)
resp = IhrmEmpCURDAPI.delete_emp(emp_id, header)
3. 接口测试用例层
# script/test_emp_add.py
import unittest
from api.ihrm_emp_crud import IhrmEmpCURDAPI
from common.assert_util import assert_util
from common.db_util import DBUtil
from config import TEL
class TestEmpAdd(unittest.TestCase):
"""添加员工测试"""
header = {
"Content-Type": "application/json",
"Authorization": "Bearer 272150b5-7b7d-4509-9ee1-b15bbdc936a9"
}
def setUp(self) -> None:
# 删除手机号
delete_sql = f"delete from bs_user where mobile = '{TEL}'"
DBUtil.uid_db(delete_sql)
def tearDown(self) -> None:
# 删除手机号
delete_sql = f"delete from bs_user where mobile = '{TEL}'"
DBUtil.uid_db(delete_sql)
# 必选参数
def test01_add_emp(self):
self._testMethodName = "必选参数"
self._testMethodDoc = "[]"
# 准备数据
json_data = {
"username": "业务猪001",
"mobile": TEL,
"workNumber": "9527"
}
# 调用自己封装的 接口
resp = IhrmEmpCURDAPI.add_emp(self.header, json_data)
print("添加-必选:", resp.json())
# 断言
assert_util(self, resp, 200, True, 10000, "操作成功")
# 组合参数
def test02_add_emp(self):
self._testMethodName = "组合参数"
self._testMethodDoc = "[]"
# 准备数据
json_data = {
"username": "业务猪001",
"mobile": TEL,
"workNumber": "9527",
"formOfEmployment": "2"
}
# 调用自己封装的 接口
resp = IhrmEmpCURDAPI.add_emp(self.header, json_data)
print("添加-组合:", resp.json())
# 断言
assert_util(self, resp, 200, True, 10000, "操作成功")
# 全部参数
def test03_add_emp(self):
self._testMethodName = "全部参数"
self._testMethodDoc = "[]"
# 准备数据
json_data = {
"username": "大猪乔治",
"mobile": TEL,
"timeOfEntry": "2021-12-01",
"formOfEmployment": 1,
"workNumber": "777888",
"departmentName": "测试",
"departmentId": "1452603344685203456",
"correctionTime": "2021-12-30T16:00:00.000Z"
}
# 调用自己封装的 接口
resp = IhrmEmpCURDAPI.add_emp(self.header, json_data)
print("添加-全部:", resp.json())
# 断言
assert_util(self, resp, 200, True, 10000, "操作成功")
4. 数据库工具类封装
1. 在 common/ 下,创建 db_util.py 文件
2. 在 文件内, 实现 数据库 工具类及常用的数据库操作方法(查、增删改)
import pymysql
# 封装数据库工具类
from config import DB_CONFIG
class DBUtil(object):
# 添加类属性
conn = None
@classmethod
def __get_conn(cls):
# 判断 conn 是否为空,如果是,再创建
if cls.conn is None:
cls.conn = pymysql.connect(
**DB_CONFIG
)
# 返回 非空连接
return cls.conn
@classmethod
def __close_conn(cls):
# 判断,conn 不为空,需要关闭。
if cls.conn is not None:
cls.conn.close()
cls.conn = None
# 常用方法:查询一条
@classmethod
def select_one(cls, sql):
cursor = None
res = None
try:
# 获取连接
cls.conn = cls.__get_conn()
# 获取游标
cursor = cls.conn.cursor()
# 执行 查询语句
cursor.execute(sql)
# 提取一条结果
res = cursor.fetchone()
print(f'查询SQL:\n\t{sql}\n查询结果:\n\t{res}')
except Exception as err:
print(f"查询SQL错误:\n\tSQL: \n\t\t{sql},\n\t错误信息:\n\t\t{str(err)}")
finally:
# 关闭游标
cursor.close()
# 关闭连接
cls.__close_conn()
# 将查询sql执行的 结果,返回
return res
# 常用方法:增删改
@classmethod
def uid_db(cls, sql):
cursor = None
try:
# 获取连接
cls.conn = cls.__get_conn()
# 获取游标
cursor = cls.conn.cursor()
# 执行 uid 语句
cursor.execute(sql)
print(f"增删改SQL:\n\t{sql}\n影响行数:{cls.conn.affected_rows()}")
# 提交事务
cls.conn.commit()
except Exception as err:
# 回滚事务
cls.conn.rollback()
print(f"增删改SQL执行失败:\n\tSQL: \n\t\t{sql},\n\t错误信息:\n\t\t{str(err)}")
finally:
# 关闭游标
cursor.close()
# 关闭连接
cls.__close_conn()
if __name__ == '__main__':
res = DBUtil.select_one("select * from bookinfo;")
DBUtil.uid_db("update bookinfo set is_delete = 1 where id = 1;")
5. 解决反复修改手机号
解决思路:
1. 在 添加员工 接口测试 前(setUp),指定一个要使用的手机号,做删除 delete sql 实现!
2. 测试 添加员工 接口, 使用这个手机号。
3. 在 添加员工 接口测试 后(tearDown),再次 删除 这个手机号。delete sql 实现!
4. 将 手机号 定义成 全局手机号, 存放在 config.py 文件中。 TEL = “13900231473”
import unittest
from api.ihrm_emp_curd import IhrmEmpCURDAPI
from common.assert_util import assert_util
from common.db_util import DBUtil
from config import TEL
class TestEmpAdd(unittest.TestCase):
"""
添加员工
"""
header = {
"Content-Type": "application/json",
"Authorization": "Bearer 272150b5-7b7d-4509-9ee1-b15bbdc936a9"
}
def setUp(self) -> None:
# 删除手机号
delete_sql = f"delete from bs_user where mobile = '{TEL}'"
DBUtil.uid_db(delete_sql)
def tearDown(self) -> None:
# 删除手机号
delete_sql = f"delete from bs_user where mobile = '{TEL}'"
DBUtil.uid_db(delete_sql)
# 必选参数
def test01_add_emp(self):
# 准备数据
json_data = {
"username": "业务猪001",
"mobile": TEL,
"workNumber": "9527"
}
# 调用自己封装的 接口
resp = IhrmEmpCURDAPI.add_emp(self.header, json_data)
print("添加-必选:", resp.json())
# 断言
assert_util(self, resp, 200, True, 10000, "操作成功")
# 组合参数
def test02_add_emp(self):
# 准备数据
json_data = {
"username": "业务猪001",
"mobile": TEL,
"workNumber": "9527",
"formOfEmployment": "2"
}
# 调用自己封装的 接口
resp = IhrmEmpCURDAPI.add_emp(self.header, json_data)
print("添加-组合:", resp.json())
# 断言
assert_util(self, resp, 200, True, 10000, "操作成功")
# 全部参数
def test03_add_emp(self):
# 准备数据
json_data = {
"username": "大猪乔治",
"mobile": TEL,
"timeOfEntry": "2021-12-01",
"formOfEmployment": 1,
"workNumber": "777888",
"departmentName": "测试",
"departmentId": "1452603344685203456",
"correctionTime": "2021-12-30T16:00:00.000Z"
}
# 调用自己封装的 接口
resp = IhrmEmpCURDAPI.add_emp(self.header, json_data)
print("添加-全部:", resp.json())
# 断言
assert_util(self, resp, 200, True, 10000, "操作成功")
6. 添加员工接口参数化
完整参数化步骤:
1. 组织测试数据到 json 文件中。 格式 [{},{},{}]
2. 读取 json 数据文件中的 [{},{},{}] 数据,转换成 [(),(),()] 数据
3. 在 测试脚本中,借助 parameterized 实现参数化
1. 导包 from parameterized import parameterized
2. 在 通用测试方法上一行,添加 @parameterized.expand()
3. 给 expand() 传入 元组列表数据( 调用 自己封装的 读取 json 文件的 函数 read_json_data() )
4. 修改 通用测试方法形参,与 json 数据文件中的 key 一致。
5. 在 通用测试方法内,使用形参
json 数据文件
[ { "desc": "必选参数", "json_data": { "username": "业务猪001", "mobile": "18000000000", "workNumber": "9527" }, "stauts_code": 200, "success": true, "code": 10000, "message": "操作成功" }, { "desc": "组合参数", "json_data": { "username": "业务猪001", "mobile": "18000000000", "workNumber": "9527", "formOfEmployment": "2" }, "stauts_code": 200, "success": true, "code": 10000, "message": "操作成功" }, { "desc": "全部参数", "json_data": { "username": "大猪乔治", "mobile": "18000000000", "timeOfEntry": "2021-12-01", "formOfEmployment": 1, "workNumber": "777888", "departmentName": "测试", "departmentId": "1452603344685203456", "correctionTime": "2021-12-30T16:00:00.000Z" }, "stauts_code": 200, "success": true, "code": 10000, "message": "操作成功" } ]
# script/test_emp_add_params.py import unittest from parameterized import parameterized from api.ihrm_emp_curd import IhrmEmpCURDAPI from common.assert_util import assert_util from common.db_util import DBUtil from common.read_json_util import read_json_data from config import TEL class TestEmpAddParams(unittest.TestCase): def setUp(self) -> None: # 删除手机号 delete_sql = f"delete from bs_user where mobile = '{TEL}'" DBUtil.uid_db(delete_sql) def tearDown(self) -> None: # 删除手机号 delete_sql = f"delete from bs_user where mobile = '{TEL}'" DBUtil.uid_db(delete_sql) # 通用测试方法 - 实现参数化 @parameterized.expand(read_json_data()) def test_add_emp(self, desc, json_data, stauts_code, success, code, message): # 准备数据 header = {"Content-Type": "application/json", "Authorization": "Bearer b040daed-39c1-4302-8777-f950770c8a26"} # 调用自己封装的 接口 resp = IhrmEmpCURDAPI.add_emp(header, json_data) print(desc, ":", resp.json()) # 断言 assert_util(self, resp, stauts_code, success, code, message)
7. 提取请求头
1. 在 common/ 下 创建 get_header.py 文件
2. 在 文件内 创建 get_header() 函数,实现 登录成功,获取令牌,拼接成 请求头,返回。
3. 在 scripts/ 的测试脚本文件中,添加 setUpClass 方法,调用 get_header() 函数。 将返回值 保存到 类属性上
4. 在 使用 请求头的位置,直接从类属性获取
"""获取登录成功的令牌,拼接到请求头,返回"""
# 定义函数
import requests
from config import BASE_URL
def get_header() -> dict:
"""
获取header
:return:
"""
url = BASE_URL + "/sys/login"
data = {"mobile": "13800000002", "password": "123456"}
resp = requests.post(url=url, json=data)
print(resp.json())
# 从 响应体中,获取 data的值
token = resp.json().get("data")
# 封装header
header = {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
}
print(type(header))
return header
if __name__ == '__main__':
dict = get_header()
print(dict)
header = {
"Content-Type": "application/json",
"Authorization": "Bearer 272150b5-7b7d-4509-9ee1-b15bbdc936a9"
}
@classmethod
def setUpClass(cls) -> None:
cls.header = get_header()
@parameterized.expand(read_json_data())
def test_add_emp(self, desc, json_data, stauts_code, success, code, message):
self._testMethodName = desc
self._testMethodDoc = f"[{json.dumps(json_data)}]"
# 调用自己封装的 接口
resp = IhrmEmpCURDAPI.add_emp(self.header, json_data)
print(desc, ":", resp.json())
# 断言
assert_util(self, resp, stauts_code, success, code, message)
8. 提取项目目录
相关知识:
__file__ : 获取 当前文件的 绝对路径。
BASE_DIR = os.path.dirname(__file__) : 获取 到 当前文件的 上一级目录。
此行代码,写在 config.py 中, 可以直接获取 项目目录
项目中使用:
1. 在 config.py 文件中,添加 获取项目路径 全局变量 BASE_DIR = os.path.dirname(__file__)
2. 修改 common/ 下 read_json_util.py 文件中,读取 json 文件 函数read_json_data(),添加 参数 path_filename
3. 在 使用 read_json_data()函数 时, 拼接 json 文件路径, 传入到 函数中。
import json
# 定义函数,读取 data/xxx.json 文件
from config import BASE_DIR
def read_json_data(path_filename) -> list:
# with open("../data/ihrm_login.json", "r", encoding="utf-8") as f:
# with open("../data/add_emp.json", "r", encoding="utf-8") as f:
with open(path_filename, "r", encoding="utf-8") as f:
json_data = json.load(f)
list_data = []
for item in json_data:
tmp = tuple(item.values())
list_data.append(tmp)
# 这个 返回,坚决不能在 for 内
return list_data
if __name__ == '__main__':
ret = read_json_data(BASE_DIR + "/data/add_emp.json")
print(ret)
# 通用测试方法 - 实现参数化
@parameterized.expand(read_json_data(BASE_PATH + "/data/add_emp.json"))
def test_add_emp(self, desc, json_data, stauts_code, success, code, message):
self._testMethodName = desc
self._testMethodDoc = f"[{json.dumps(json_data)}]"
# 调用自己封装的 接口
resp = IhrmEmpCURDAPI.add_emp(self.header, json_data)
print(desc, ":", resp.json())
# 断言
assert_util(self, resp, stauts_code, success, code, message)
# 通用测试方法(实现参数化)
@parameterized.expand(read_json_data(BASE_PATH + "/data/ihrm_login.json"))
def test_login(self, desc, req_data, stauts_code, success, code, message):
self._testMethodName = desc
self._testMethodDoc = f'[{json.dumps(req_data)}]'
# 调用自己封装的接口
resp = IhrmLoginApi.login(req_data)
print(desc, ":", resp.json())
# 断言
assert_util(self, resp, stauts_code, success, code, message)
3. 生成测试报告
步骤:
1. 创建测试套件实例。 suite
2. 添加 测试类
3. 创建 HTMLTestReport 类实例。 runner
4. runner 调用 run(), 传入 suite
"""
生成测试报告的步骤:
1. 创建测试套件实例。 suite
2. 添加 测试类
3. 创建 HTMLTestReport 类实例。 runner
4. runner 调用 run(), 传入 suite
"""
import unittest
from htmltestreport import HTMLTestReport
from scripts.test_emp_add_params import TestEmpAddParams
from scripts.test_ihrm_login_params import TestIhrmLoginParams
# 1. 创建测试套件实例。 suite
suite = unittest.TestSuite()
# 2. 添加 测试类, 组装测试用例
suite.addTest(unittest.makeSuite(TestIhrmLoginParams))
suite.addTest(unittest.makeSuite(TestEmpAddParams))
# 3. 创建 HTMLTestReport 类实例。 runner
# runner = HTMLTestReport("./report/ihrm.html", description="描述", title="标题") # 相对路径
runner = HTMLTestReport(BASE_DIR + "/report/ihrm.html") # 绝对路径
# 4. runner 调用 run(), 传入 suite
runner.run(suite)
当前代码位置:test_code\ae_apiAutoTestFrameWork_a\apitest_ihrm_a