1,接口自动化框架
1.1框架结构
接口测试框架的结构如下图所示:
接口测试框架的结构说明:
- API 用来封装被测系统的接口
- TestCase 将一个或者多个接口封装成测试用例,并使用UnitTest管理测试用例TestCase 可以调用数据库进行数据校验
- 为了方便维护测试数据,可以把测试脚本和测试数据分离开
- 通过UnitTest断言接口返回的数据,并生成测试报告
1.2 框架目录结构
apiTestFramework #项目名称
-api #package:定义封装被测系统的接口
-script #package:定义测试用例脚本
-data #dir:存放测试数据
-report #dir:存放生成测试报告
-lib #package:存放第三方的文件
-setting.py #定义项目的配置信息
-utils.py #定义工具类
-run_suite.py #执行测试套件的入口
1.3 封装TPShop登录接口案例
按照功能模块定义封装被测系统的接口,方便测试脚本的调用,并且能够到达代码的复用,对登录功能的相关接口进行封装,示例代码:
# api/login.py
class LoginApi:
def __init__(self):
self.verify_code_url = "http://locolhost/index.php?m=Home&a=verify"
self.login_url = "http://locolhost/index.php?m=Home&c=User&a=do_login"
#获取验证码
def get_login_verify_code(self,session):
return session.get(self.verify_code_url)
#登录
def login(self,session,username,password,verify_code):
# 发送请求
data = {
"username":username,
"password":password,
"verify_code":verify_code,
}
return session.post(self.login,data=data)
1.4 登录测试用例
将api模块中的一个或多个接口封装成一个测试用例,并使用测试框架UnitTest测试用例,定义登录功能的测试用例,示例代码:
class TestLogin(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.login_api = LoginApi()
def setUp(self) -> None:
self.session = Session()
def tearDown(self) -> None:
self.session.close()
#登录成功: 到底干了啥
#发送请求(调api中的封装请求方法) --> 响应
#断言
def test_login_success(self):
#获取验证码
response = self.login_api.get_login_verify_code(self.seccion)
#判断是否为图片类型
self.assertIn("",response.headers.get("Content-Type"))
#登录
response = self.login_api.login(self.session,"13012345678","123456","8888")
res = response.json()
#断言
self.assertEqual(200,res.status_code)
self.assertEqual(1,res.get("status"))
self.assertEqual("登陆成功",res.get("msg"))
#账号不存在
def test_login_username_not_exist(self):
#获取验证码
response = self.login_api.get_login_verify_code(self.seccion)
#判断是否为图片类型
self.assertIn("",response.headers.get("Content-Type"))
#登录
response = self.login_api.login(self.session,"13088888888","123456","8888")
res = response.json()
#断言
self.assertEqual(200,res.status_code)
self.assertEqual(-1,res.get("status"))
self.assertEqual("账号不存在",res.get("msg"))
#账号不存在
def test_login_username_not_exist(self):
#获取验证码
response = self.login_api.get_login_verify_code(self.seccion)
#判断是否为图片类型
self.assertIn("",response.headers.get("Content-Type"))
#登录
response = self.login_api.login(self.session,"13012345678","error","8888")
res = response.json()
#断言
self.assertEqual(200,res.status_code)
self.assertEqual(-2,res.get("status"))
self.assertEqual("密码错误",res.get("msg"))
1.5 集成测试报告
使用HTMLTestRunner生成HTML格式的测试报告
注意:需要将HTMLTestRunner.py工具放置在lib中 (代码如下)
import time
import unittest
from script.test_login import TestLogin
from tools.HTMLTestRunner import HTMLTestRunner
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestLogin))
#测试报告文件路径
report_file = "./report/report.html".format(time.strftime("%Y%m%d-%H%M%S")) with open(report_file,"wb") as f:
# 创建HTMLTestRunner运行器
runner = HTMLTestRunner(f,title="TPshop接口自动化测试报告",description="V1.0")
# 运行测试条件
runner.run(suite)
2,参数化处理
2.1 参数化分析
1,分析需要参数化的参数,都有哪些?
输入数据:用户名,密码,验证码
预期结果:content_type,
状态码,业务码,业务消息
2,选择数据承载格式
json
db
3,修改测试用例中的代码
3.1 构建参数化数据(build_data)
3.2 在测试用例前引用参数化(@parameter)
3.3 在具体的测试用例中,按照获得的数据进行参数替换
2.2 构建json文件存放测试数据(例如:下图)
[
{
"username": "13012345678",
"password": "13579",
"verify_code": "8888",
"content_type":"image",
"status_code":200,
"status": 0,
"msg": "账号或密码登陆错误"
},
{
"username": "",
"password": "13579",
"verify_code": "8888",
"content_type":"image",
"status_code":200,
"status": 0,
"msg": "账号或密码登陆错误"
},
{
"username": "13800000005",
"password": "",
"verify_code": "8888",
"content_type":"image",
"status_code":200,
"status": 0,
"msg": "账号或密码登陆错误"
}
]
2.3 构建参数化数据
def build_data():
with open('./data/data.json','r',encoding='utf-8') as f:
content = f.read()
data = json.loads(content)
d = []
for item in data:
d.append(
(
item["username"],
item["password"],
item["verify_code"],
item["status_code"]
item["status"],
item["msg"]
)
)
return d
2.4 测试用例实现参数化
class TestLogin(unittest.TestCase):
# 前置处理
def setUp(self):
self.login_api = LoginAPI() #实例化接口类
self.session = requests.Session() #创建session对象
# 后置处理
def tearDown(self):
if self.session:
self.session.close()
@parameterized.expand(build_data)
# 创建测试用例
def test01_login(self,username,password,verify_code,content_type,status_code,status,msg):
#调用验证码接口获取验证,并进行断言
response = self.login_api.get_verify_code(self.session)
self.assertEqual(status_code,response.status_code)
self.assertIn(status_type,response.headers.get("Content-Type"))
#调用登录接口获取登录信息,并进行断言
response = self.login_api.login(self.session,username,password,verify_code)
self.assertEqual(status_code,response.status_code)
self.assertEqual(status,response.json().get("status"))
self.assertIn(msg,response.json().get("msg"))
3,db数据承载形式实现参数化
3.1 构建数据库数据
执行SQL脚本,将数据写入数据库中
3.2 修改构建数据函数
def build_data():
#获取数据库数据
sql = "select * from t_login"
db_data = DBUtil.exe_sql(sql)
test_data = []
for case_data in db_data:
username = case_data[2]
password = case_data[3]
verify_code = case_data[4]
content_type = case_data[5]
status_code= case_data[6]
status = case_data[7]
msg = case_data[8]
test_data.append(
(
username,password,verify_code,content_type,status_code,status,msg
)
)
return test_data
拓展(只获取列表中的字典中的指定字段)
def build_data():
data_json = [{"username":"xxx","password":"123","age":12,"gender":"男"}] #假设有100多个键值对
dic = {"username":"xxx","age":12} #只需要两个键值对
list = []
for i in data_json:
dic = {"username":"","age":""}
for key in dic:
dic[key] = i["key"]
list.append(dic)