python实现接口自动化
代码实现自动化相关理论
-
代码编写脚本和工具实现脚本区别是啥?
- 代码:
- 优点:代码灵活方便
- 缺点:学习成本高
- 工具:
- 优点:易上手
- 缺点:灵活度低,有局限性。
- 总结:
- 功能脚本:工具
- 自动化脚本:代码
- 代码:
-
代码接口自动化怎么做的?
第一步:python+request+unittest; 具体描述? 第二步:封装、调用、数据驱动、日志、报告; 详细举例: 第三步:api\scripts\data\log\report\until...
脚本实现
- 使用代码编写自动化脚本的流程
1、抽取功能用例转为自动化用例
2、搭建环境(测试工具、)
3、搭建目录结构
4、编写脚本
5、执行脚本
6、配置持续集成
抽取功能转为自动化用例
搭建环境(测试工具)
1、python、Pycharm、requests、pymysql、parametrize
2、jenkins、jdk
提示:由于编写的自动化脚本,而自动化脚本编写之前功能已测试完毕,所以不需要在单独搭建项目环境。
搭建目录结构
代码编写
-
api(api_register_login.py)
from config import Host class ApiRegisterLogin:#初始化def __init__(self,session):# 获取session对象self.session =session# 图片验证码urlself.__url_img_code=Host+"/common/public/verifycode1/{}"# 短信验证码urlself.__url_phone_code = Host + "/member/public/sendSms"# 注册urlself.__url_register= Host + "/member/public/reg"# 登录urlself.__url_login=Host + "/member/public/login"# 登录状态urlself.__url_login_status = Host + "/member/public/islogin"#1.获取图片验证码接口 封装def api_img_code(self):pass#2.获取短信验证码接口 封装def api_phone_code(self):pass#3.注册接口封装def api_register(self):pass#4.登录接口封装def api_login(self):pass#5.查询登录状态接口 封装def api_login_status(self):pass
-
实现
#1.获取图片验证码接口 封装def api_img_code(self,random):# 调用get方法,返回响应对象return self.session.get(url=self.__url_img_code.format(random))#2.获取短信验证码接口 封装def api_phone_code(self,phone,imgVerifyCode):#1.定义请求参数data={"phone":phone,"imgVerifyCode":imgVerifyCode,"type":"reg"}# 调用请求方法return self.session.post(url=self.__url_phone_code,data=data)#3.注册接口封装def api_register(self,phone,password,imgVerifyCode,phone_code):# 1.定义请求参数data = {"phone": phone,"password":password,"imgVerifyCode": imgVerifyCode,"phone_code":phone_code,"dy_server":"on","invite_phone": ""}# 调用请求方法return self.session.post(url=self.__url_register, data=data)#4.登录接口封装def api_login(self,phone,password):#1.定义请求参数data={"phone":phone,"password":password,}# 调用请求方法return self.session.post(url=self.__url_login,data=data)#5.查询登录状态接口 封装def api_login_status(self):return self.session.post(url=self.__url_login_status)
- script(test01_register_login.py)
import unittest
import requests
from api.api_register_login import ApiRegisterLogin
class TestRegisterLogin(unittest.TestCase):#初始化def setUp(self) -> None:# 获取session对象self.session=requests.session()#获取ApiRegisterLogin实例self.reg=ApiRegisterLogin(self.session)#结束def tearDownClass(cls) -> None:# 关闭session对象self.session.close()#1.获取图片验证码接口 测试def test01_img_code(self):pass#2.获取短信验证码接口 测试def test02_phone_code(self):pass#3.注册接口 测试def test03_register(self):pass#4.登录接口 测试def test04_login(self):pass#5.查询登录状态接口 测试def test05_login_status(self):pass
- 封装
import unittest
import requests
from api.api_register_login import ApiRegisterLogin
class TestRegisterLogin(unittest.TestCase):#初始化def setUp(self) -> None:# 获取session对象self.session=requests.session()#获取ApiRegisterLogin实例self.reg=ApiRegisterLogin(self.session)#结束def tearDown(self) -> None:# 关闭session对象self.session.close()#1.获取图片验证码接口 测试def test01_img_code(self):# 调用图片验证码r= self.reg.api_img_code(123)#查看响应状态码print(r.status_code)#2.获取短信验证码接口 测试def test02_phone_code(self,phone=13312345678,imgVerifyCode=8888):#1.调用获取短信验证码接口--目的:让session对象记录cookieself.reg.api_img_code(123)#2.调用短信验证码接口r=self.reg.api_phone_code(phone=phone,imgVerifyCode=imgVerifyCode)print(r.json())#3.注册接口 测试def test03_register(self,phone=13312345678,imgVerifyCode=8888,password="test123",phone_code=666666):#1.图片验证码接口self.reg.api_img_code(123)#2.短信验证码接口self.reg.api_phone_code(phone=phone, imgVerifyCode=imgVerifyCode)#3.注册接口r=self.reg.api_register(phone=phone, password=password,verifycode=imgVerifyCode,phone_code=phone_code)#4.查看结果print(r.json())#4.登录接口 测试def test04_login(self,keywords="13312345678",password="test123"):#1.调用登录接口r = self.reg.api_login(keywords=keywords, password=password)# 2.查看结果print(r.json())#5.查询登录状态接口 测试def test05_login_status(self):#调用登录接口self.reg.api_login(keywords="13312345678",password="test123")#调用查询登录接口状态r = self.reg.api_login_status()#看结果print(r.json())
断言
- 说明:判断程序执行实际结果是否符合预期结果
- 示例:
import unittest
import requests
from api.api_register_login import ApiRegisterLogin
class TestRegisterLogin(unittest.TestCase):#初始化def setUp(self) -> None:# 获取session对象self.session=requests.session()#获取ApiRegisterLogin实例self.reg=ApiRegisterLogin(self.session)#结束def tearDown(self) -> None:# 关闭session对象self.session.close()#1.获取图片验证码接口 测试def test01_img_code(self):try:# 调用图片验证码r= self.reg.api_img_code(123)#查看响应状态码self.assertEqual(200,r.status_code)except Exception as e:#日志print(e)#抛异常raise#2.获取短信验证码接口 测试def test02_phone_code(self,phone=13312345678,imgVerifyCode=8888,expect_text="发送成功"):try:#1.调用获取短信验证码接口--目的:让session对象记录cookieself.reg.api_img_code(123)#2.调用短信验证码接口r=self.reg.api_phone_code(phone=phone,imgVerifyCode=imgVerifyCode)#3.查看响应结果print(r.json())#self.assertIn("发送成功",r.json().get("description"))self.assertIn(expect_text,r.text)except Exception as e:#日志print(e)#抛异常raise#3.注册接口 测试def test03_register(self,phone=13312345679,imgVerifyCode=8888,password="test123",phone_code=666666,expect_text="注册成功"):try:#1.图片验证码接口self.reg.api_img_code(123)#2.短信验证码接口self.reg.api_phone_code(phone=phone, imgVerifyCode=imgVerifyCode)#3.注册接口r=self.reg.api_register(phone=phone, password=password,verifycode=imgVerifyCode,phone_code=phone_code)#4.查看结果print(r.json())self.assertIn(expect_text, r.text)except Exception as e:#日志print(e)#抛异常raise#4.登录接口 测试def test04_login(self,keywords="13312345678",password="test123",expect_text="登录成功"):try:#1.调用登录接口r = self.reg.api_login(keywords=keywords, password=password)# 2.查看结果print(r.json())self.assertIn(expect_text, r.text)except Exception as e:#日志print(e)#抛异常raise#5.查询登录状态接口 测试def test05_login_status(self,expect_text="OK"):try:#调用登录接口self.reg.api_login(keywords="13312345678",password="test123")#调用查询登录接口状态r = self.reg.api_login_status()#看结果print(r.json())self.assertIn(expect_text, r.text)except Exception as e:#日志print(e)#抛异常raise
提示:捕获异常的目的是为了将错误信息记录下来,捕获信息完成后,必须抛出异常。
参数化
- 步骤
1.编写数据存储文件json
2.编写读取工具方法 read_json()
3.使用参数化组件进行引用 parametrize
- 1.编写参数化文件(register_login.json)
心得:
1、根据模块来新建json文件(1个模块1个json文件)
2、最外侧使用{},模块下几个接口,编写几个key,值为列表
3、列表值中,有几组数据,就写几个{}
4、每组数据{}中,组成格式:说明+参数+预期结果
{"img_code": [{"desc": "获取图片验证码成功(随机小数)","random": 0.123,"expect_code": 200},{"desc": "获取图片验证码成功(随机整数)","random": 1,"expect_code": 200},{"desc": "获取图片验证码成功(随机为空)","random": " ","expect_code": 404},{"desc": "获取图片验证码成功(随机数为字符串)","random": "a123","expect_code": 400}],"phone_code": [{"desc":"获取短信验证码成功 ","phone":"16612345678","imgVerifyCode":8888,"expect_code": "发送成功"},{"desc":"获取短信验证码成功 ","phone":"16612345678","imgVerifyCode":8889,"expect_code": "验证码错误"}],"register": [{"desc":"注册成功(必填参数) ","phone":"16612341111","password":"test123","verifycode":8888,"phone_code": 666666,"expect_code": "注册成功"},{"desc":"注册失败(图片验证码错误)","phone":"16612341111","password":"test123","verifycode":8889,"phone_code": 666666,"expect_code": "验证码错误!"},{"desc":"注册失败(短信验证码错误)","phone":"16612341111","password":"test123","verifycode":8888,"phone_code": 666667,"expect_code": "验证码错误"},{"desc":"注册失败(手机号已存在)","phone":"13312345678","password":"test123","verifycode":8888,"phone_code": 666666,"expect_code": "已存在"}],"login": [{"desc": "登录成功","keywords": "13312345678","password":"test123","expect_code": "登录成功"},{"desc": "登录失败(密码为空)","keywords": "13312345678","password":"","expect_code": "不能为空"},{"desc": "登录失败(解锁)","keywords": "13312345678","password":"error123","expect_code": "登录成功"}],"login_status": [{"desc": "查询登录状态(已登录) ","status": "已登录","expect_code": "OK"},{"desc": "查询登录状态(未登录) ","status": "未登录","expect_code": "未登"}]
}
- 编写读取数据工具(util.py)
#读取json工具
import os
import jsonfrom config import DIR_PATH
def read_json(filename,key):#拼接读取文件的完整路径 os.sep动态获取/\file_path=DIR_PATH+os.sep+"data"+os.sep+filenamearr=[]with open(file_path,"r",encoding="utf-8") as f:for data in json.load(f).get(key):arr.append(tuple(data.values())[1:])return arrif __name__ == '__main__':print(read_json("register_login.json", "register"))
-
参数化引用
- 难点1:错误次数锁定
# 如果 password=="error123":i= 1while i<=3:#调用登录r=self.xxxlogin()#改变计数器i+=1#断言锁定#暂停60秒#测试登录成功(注意:登录时必须给正确密码)r= self.reg.api_login(keywords="13312345678",password="test123")else:#调用登录(参数数据)#断言
- 难点2:查询登录状态,不同结果。
#如果 status=="已登录":# 调用登录#调用查询登录状态接口#断言
断言代码示例:(test01_register_login.py)
import unittest
import requests
from time import sleep
from parameterized import parameterized
from api.api_register_login import ApiRegisterLogin
from util import read_json
class TestRegisterLogin(unittest.TestCase):#初始化def setUp(self) -> None:# 获取session对象self.session=requests.session()#获取ApiRegisterLogin实例self.reg=ApiRegisterLogin(self.session)#结束def tearDown(self) -> None:# 关闭session对象self.session.close()#1.获取图片验证码接口 测试@parameterized.expand(read_json("register_login.json", "img_code"))def test01_img_code(self,random,expect_code):try:# 调用图片验证码r= self.reg.api_img_code(random)#查看响应状态码self.assertEqual(expect_code,r.status_code)except Exception as e:#日志print(e)#抛异常raise#2.获取短信验证码接口 测试@parameterized.expand(read_json("register_login.json", "phone_code"))def test02_phone_code(self,phone,imgVerifyCode,expect_text):try:#1.调用获取短信验证码接口--目的:让session对象记录cookieself.reg.api_img_code(123)#2.调用短信验证码接口r=self.reg.api_phone_code(phone=phone,imgVerifyCode=imgVerifyCode)#3.查看响应结果print(r.json())#self.assertIn("发送成功",r.json().get("description"))self.assertIn(expect_text,r.text)except Exception as e:#日志print(e)#抛异常raise#3.注册接口 测试@parameterized.expand(read_json("register_login.json", "register"))def test03_register(self,phone,password,imgVerifyCode,phone_code,expect_text):try:#1.图片验证码接口self.reg.api_img_code(123)#2.短信验证码接口self.reg.api_phone_code(phone=phone, imgVerifyCode=imgVerifyCode)#3.注册接口r=self.reg.api_register(phone=phone, password=password,verifycode=imgVerifyCode,phone_code=phone_code)#4.查看结果print(r.json())self.assertIn(expect_text, r.text)except Exception as e:#日志print(e)#抛异常raise@parameterized.expand(read_json("register_login.json", "login"))#4.登录接口 测试def test04_login(self,keywords,password,expect_text):try:i=1r=Noneif "error" in password:while i<=3:r=self.reg.api_login(keywords,password)i+=1#断言锁定print("测试锁定:",r.text)self.assertIn("锁定",r.text)#暂停60秒sleep(60)#测试登录成功r= self.reg.api_login(keywords="13312345678",password="test123")# 断言登录成功self.assertIn(expect_text,r.text)else:#1.调用登录接口r = self.reg.api_login(keywords=keywords, password=password)# 2.查看结果print(r.json())self.assertIn(expect_text, r.text)except Exception as e:#日志print(e)#抛异常raise@parameterized.expand(read_json("register_login.json", "login_status"))#5.查询登录状态接口 测试def test05_login_status(self,status,expect_text):try:if status=="已登录":#调用登录接口self.reg.api_login(keywords="13312345678",password="test123")#调用查询登录接口状态r = self.reg.api_login_status()#看结果print(r.json())self.assertIn(expect_text, r.text)except Exception as e:#日志print(e)#抛异常raise
日志
-
日志的作用?
记录程序运行的步骤和错误。
日志的场景
-
1、调试bug 2、查看程序运行轨迹
-
日志的基本应用
#1、导包 import logging #2、调用日志入口 logging.error("出错啦,错误原因:{}".format(e))
import logging #设置日志级别及保存文件名 logging.basicConfig(level=logging.DEBUG,filename="../log/p2p.log") #调用日志 logging.debug("调试信息") logging.info("信息级别") logging.warning("警告") logging.error(("断言错误!")) logging.critical("严重错误")
-
测试人员使用的日志的入口
info:记录运行步骤 error:记录运行错误
-
日志底层组成介绍
说明:logging库底层有4大组件(日志器、处理器、格式器、过滤器) 1、日志器:接受日志信息,设置日志显示级别 2、处理器:控制日志显示位置或文件 3、格式器:控制日志输出的显示样式 关系: 格式器必须关联处理器 处理器必须关联日志器
-
日志封装应用
重组封装的目的:解决日志显示的样式、存储方式
import logging.handlers
# 日志工具
class GetLog:@classmethoddef get_log(cls):cls.log = Noneif cls.log is None:#1.获取日志器cls.log =logging.getLogger()#设置日志级别infocls.log.setLevel(logging.INFO)file_path = DIR_PATH + os.sep + "log" + os.sep + "p2p.log"#2.获取处理器TimedRotatingFileHandler:日志保存到文件且根据时间去分隔tf = logging.handlers.TimedRotatingFileHandler(filename=file_path,when="midnight",interval=1,backupCount=3,encoding="utf-8")#3.获取格式器fmt ="%(asctime)s %(levelname)s[%(filename)s(%(funcName)s:%(lineno)d)]-%(message)s"fm = logging.Formatter(fmt)#4.将格式器添加到处理器中tf.setFormatter(fm)#5.将处理器添加到日志器中cls.log.addHandler(tf)#返回日志器return cls.logif __name__ == '__main__':GetLog.get_log().info("信息级别测试")
- 应用(init.py)
应用的级别:info、error
info:info记录程序运行的步骤
error:记录程序错误
from api import log
from config import Hostclass ApiRegisterLogin:#初始化def __init__(self,session):# 获取session对象self.session =session# 图片验证码urlself.__url_img_code=Host+"/common/public/verifycode1/{}"# 短信验证码urlself.__url_phone_code = Host + "/member/public/sendSms"# 注册urlself.__url_register= Host + "/member/public/reg"# 登录urlself.__url_login=Host + "/member/public/login"# 登录状态urlself.__url_login_status = Host + "/member/public/islogin"#1.获取图片验证码接口 封装def api_img_code(self,random):log.info("正在调用获取图片验证码接口,请求方法:{},请求url:{}".format("get",self.__url_img_code.format(random)))# 调用get方法,返回响应对象return self.session.get(url=self.__url_img_code.format(random))#2.获取短信验证码接口 封装def api_phone_code(self,phone,imgVerifyCode):#1.定义请求参数data={"phone":phone,"imgVerifyCode":imgVerifyCode,"type":"reg"}log.info("正在调用短信验证码接口,请求方法:{},请求url:{},请求参数:{}".format("post",self.__url_phone_code,data=data))# 调用请求方法return self.session.post(url=self.__url_phone_code,data=data)#3.注册接口封装def api_register(self,phone,password,verifycode,phone_code):# 1.定义请求参数data = {"phone": phone,"password":password,"verifycode": verifycode,"phone_code":phone_code,"dy_server":"on","invite_phone": ""}log.info("正在调用注册接口,请求方法:{},请求url:{},请求参数:{}".format("post",self.__url_register, data=data))# 调用请求方法return self.session.post(url=self.__url_register, data=data)#4.登录接口封装def api_login(self,keywords,password):#1.定义请求参数data={"keywords":keywords,"password":password,}log.info("正在调用登录接口,请求方法:{},请求url:{},请求参数:{}".format("post",self.__url_login,data=data))# 调用请求方法return self.session.post(url=self.__url_login,data=data)#5.查询登录状态接口 封装def api_login_status(self):log.info("正在调用查询登录状态接口,请求方法:{},请求url:{}".format("post",self.__url_login_status))return self.session.post(url=self.__url_login_status)
import unittest
import requests
import logging
from api import log
from time import sleep
from parameterized import parameterized
from api.api_register_login import ApiRegisterLogin
from util import read_json
class TestRegisterLogin(unittest.TestCase):#初始化def setUp(self) -> None:# 获取session对象self.session=requests.session()log.info("正在初始化session对象:{}".format(self.session))#获取ApiRegisterLogin实例self.reg=ApiRegisterLogin(self.session)#结束def tearDown(self) -> None:# 关闭session对象self.session.close()log.info("正在关闭session对象:{}".format(self.session))#1.获取图片验证码接口 测试@parameterized.expand(read_json("register_login.json", "img_code"))def test01_img_code(self,random,expect_code):try:# 调用图片验证码r= self.reg.api_img_code(random)log.info("执行图片验证码响应状态码:{}".format(r.status_code))#查看响应状态码self.assertEqual(expect_code,r.status_code)log.info("执行图片验证码断言通过")except Exception as e:#日志log.error("断言失败,原因:{}".format(e))#抛异常raise#2.获取短信验证码接口 测试@parameterized.expand(read_json("register_login.json", "phone_code"))def test02_phone_code(self,phone,imgVerifyCode,expect_text):try:#1.调用获取短信验证码接口--目的:让session对象记录cookieself.reg.api_img_code(123)#2.调用短信验证码接口r=self.reg.api_phone_code(phone=phone,imgVerifyCode=imgVerifyCode)log.info("执行结果接口为:{}".format(r.text))#3.查看响应结果print(r.json())#self.assertIn("发送成功",r.json().get("description"))self.assertIn(expect_text,r.text)log.info("执行断言通过")except Exception as e:#日志log.error("断言失败,原因:{}".format(e))#抛异常raise#3.注册接口 测试@parameterized.expand(read_json("register_login.json", "register"))def test03_register(self,phone,password,imgVerifyCode,phone_code,expect_text):try:#1.图片验证码接口self.reg.api_img_code(123)#2.短信验证码接口self.reg.api_phone_code(phone=phone, imgVerifyCode=imgVerifyCode)#3.注册接口r=self.reg.api_register(phone=phone, password=password,verifycode=imgVerifyCode,phone_code=phone_code)log.info("执行结果接口为:{}".format(r.text))#4.查看结果print(r.json())self.assertIn(expect_text, r.text)log.info("执行断言通过")except Exception as e:#日志log.error("断言失败,原因:{}".format(e))#抛异常raise@parameterized.expand(read_json("register_login.json", "login"))#4.登录接口 测试def test04_login(self,keywords,password,expect_text):try:i=1r=Noneif "error" in password:while i<=3:r=self.reg.api_login(keywords,password)i+=1#断言锁定print("测试锁定:",r.text)self.assertIn("锁定",r.text)#暂停60秒sleep(60)#测试登录成功r= self.reg.api_login(keywords="13312345678",password="test123")# 断言登录成功self.assertIn(expect_text,r.text)else:#1.调用登录接口r = self.reg.api_login(keywords=keywords, password=password)log.info("执行结果接口为:{}".format(r.text))# 2.查看结果print(r.json())self.assertIn(expect_text, r.text)log.info("执行断言通过")except Exception as e:#日志log.error("断言失败,原因:{}".format(e))#抛异常raise@parameterized.expand(read_json("register_login.json", "login_status"))#5.查询登录状态接口 测试def test05_login_status(self,status,expect_text):try:if status=="已登录":#调用登录接口self.reg.api_login(keywords="13312345678",password="test123")#调用查询登录接口状态r = self.reg.api_login_status()log.info("执行结果接口为:{}".format(r.text))#看结果print(r.json())self.assertIn(expect_text, r.text)log.info("执行断言通过")except Exception as e:#日志log.error("断言失败,原因:{}".format(e))#抛异常raise
认证开户
封装(api_approve_trust.py)
from config import Host
class ApiApproveTrust:# 初始化def __init__(self,session):#获取session对象self.session=session# 认证urlself.__url_approve = Host+"/member/realname/approverealname"# 查询认证状态urlself.__url_approve_status = Host+"/member/member/getapprove"#开户urlself.__url_trust = Host + "/trust/trust/register"#图片验证码urlself.__url_img_code = Host + "/common/public/verifycode1/{}"#充值urlself.__url_recharge = Host + "/trust/trust/recharge"# 1.认证接口 封装def api_approve(self):pass#2.查询认证状态接口 封装def api_approve_status(self):pass#3.开户接口 封装def api_trust(self):pass#4.获取图片验证码接口 封装def api_img_code(self):pass#5.充值接口 封装def api_recharge(self):pass
- 难点:认证接口请求参数类型为:multipart/form-data多消息类型,如何实现?
解决:请求使用data+files两种参数格式,消息头会自动切换到multipart接口
示例:self.session.post(url=self.__url_approve,data=data,files={"x":"y"})
from config import Host
class ApiApproveTrust:# 初始化def __init__(self,session):#获取session对象self.session=session# 认证urlself.__url_approve = Host+"/member/realname/approverealname"# 查询认证状态urlself.__url_approve_status = Host+"/member/member/getapprove"#开户urlself.__url_trust = Host + "/trust/trust/register"#图片验证码urlself.__url_img_code = Host + "/common/public/verifycode1/{}"#充值urlself.__url_recharge = Host + "/trust/trust/recharge"# 1.认证接口 封装def api_approve(self):#1.请求参数data={"realname":"华仔","card_id":"110101200007289809"}#2.调用请求方法 难题:multipart/form-data使用:data+files来实现多消息体类型return self.session.post(url=self.__url_approve,data=data,files={"x":"y"})#2.查询认证状态接口 封装def api_approve_status(self):return self.session.post(url=self.__url_approve_status)#3.开户接口 封装def api_trust(self):return self.session.post(url=self.__url_trust)#4.获取图片验证码接口 封装def api_img_code(self,random):return self.session.get(url=self.__url_img_code.format(random))#5.充值接口 封装def api_recharge(self,valicode):# 1.请求参数data = {"paymentType": "chinapnrTrust","amount": "10000","formStr":"reForm","valicode":valicode}# 2.调用请求方法return self.session.post(url=self.__url_recharge, data=data)
调用(test02_approve_trust.py)
- 结构
import unittest
import requests
from api.api_approve_trust import ApiApproveTrust
from api.api_register_login import ApiRegisterLogin
class TestApproveTrust(unittest.TestCase):#初始化def setUp(self) -> None:#1. 获取sessionself.session=requests.session()#2.获取ApiApproveTrust对象self.ApiApproveTrust=ApiApproveTrust(self.session)#3.调用登录成功ApiRegisterLogin(self.session).api_login()# 结束def tearDown(self) -> None:self.session.close()#1、认证接口 测试def test01_approve(self):pass#2、查询认证状态接口 测试def test_02_status(self):pass#3、开户接口 测试def test03_trust(self):pass#4、获取图片验证码 测试def test04_img_code(self):pass#5、充值接口 测试def test05_recharge(self):pass
- 实现
import unittest
import requests
from api import log
from api.api_approve_trust import ApiApproveTrust
from api.api_register_login import ApiRegisterLogin
class TestApproveTrust(unittest.TestCase):#初始化def setUp(self) -> None:#1. 获取sessionself.session=requests.session()#2.获取ApiApproveTrust对象self.approve=ApiApproveTrust(self.session)#3.调用登录成功ApiRegisterLogin(self.session).api_login()# 结束def tearDown(self) -> None:self.session.close()#1、认证接口 测试def test01_approve(self):#调用认证接口try:r=self.approve.api_approve()log.info("接口执行结果为:{}".format(r.text))#断言self.assertIn("提交成功",r.text)log.info("断言通过")except Exception as e:# 日志log.error("断言错误!原因:{}".format(e))# 抛异常raise#2、查询认证状态接口 测试def test_02_status(self):# 调用查询认证状态接口try:r = self.approve.api_approve_status()log.info("接口执行结果为:{}".format(r.text))# 断言self.assertIn("华", r.text)log.info("断言通过")except Exception as e:# 日志log.error("断言错误!原因:{}".format(e))# 抛异常raise#3、开户接口 测试def test03_trust(self):# 调用开户接口try:r = self.approve.api_trust()log.info("接口执行结果为:{}".format(r.json()))# 断言self.assertIn("form", r.text)print("请求后台开户结果为:", r.json())log.info("断言通过!")#三方开户except Exception as e:# 日志log.error("断言错误!原因:{}".format(e))# 抛异常raise#4、获取图片验证码 测试def test04_img_code(self,random=123):# 调用获取图片验证码接口try:r = self.approve.api_img_code(random)log.info("接口执行结果为:{}".format(r.status_code))# 断言self.assertEqual(200, r.status_code)log.info("断言通过")except Exception as e:# 日志log.error("断言错误!原因:{}".format(e))# 抛异常raise#5、充值接口 测试def test05_recharge(self,valicode=8888):# 调用充值接口try:#调用图片验证码 获取cookieself.approve.api_img_code(123)r = self.approve.api_recharge(valicode)log.info("接口执行结果为:{}".format(r.json()))print("请求后台充值结果为:{}".format(r.json()))# 断言self.assertIn("form", r.text)log.info("断言通过")# 三方充值except Exception as e:# 日志log.error("断言错误!原因:{}".format(e))# 抛异常raise
问题:
三方开户、三方充值如何操作?--从响应数据中提取input标签name属性值和value属性,在python中如何提取?
BeautifSoup库
-
一个python解析html/xml的三方库
-
安装
pip install beautifulsoup4
-
应用步骤:
1、导包 2、实例化 3、调用方法
-
示例:
#1、导包
from bs4 import BeautifulSoup
html="""<html><head><title>某马程序员</title></head><body><p id ="test01">软件测试</p><p id ="test02">2024年</p><a href="/api.html">接口测试</a><a href="/web.html">Web自动化测试</a><a href="/app.html">App自动化测试</a></body></html>
"""
#2、获取bs对象 告诉BeautifulSoup类 你要解析的是html格式
bs=BeautifulSoup(html,"html.parser")
#3、调用方法
"""重点:
1、查找所有标签 bs.find_all("标签名")==元素的集合==("元素1","元素2")
2、查找属性 元素.get("属性名")
"""
for a in bs.find_all('a'):print(a.get("href"))#4、扩展其他方法
#获取单个元素
print(bs.a)
#获取文本
print(bs.a.string)
#获取属性
print(bs.a.get("href"))
#获取标签名
print(bs.a.name)
- 重点
1、查找所有标签 bs.find_all("标签名")==元素的集合==("元素1","元素2")
2、查找属性 元素.get("属性名")
- 其他方法
#4、扩展其他方法
#获取单个元素
print(bs.a)
#获取文本
print(bs.a.string)
#获取属性
print(bs.a.get("href"))
#获取标签名
print(bs.a.name)
提取html数据根据封装
- 思路
# 提取html
# 获取BeautifulSoup对象
# 提取url
# 遍历查找所有的input标签
# 提取name和value的值,并组装到新的字典中
# 返回url和字段
- 示例
# 提取html数据
def parser_html(result):#1、提取htmlhtml =result.json().get("description").get("form")#2、获取bs对象bs=BeautifulSoup(html,"html.parser")#3、提取urlurl= bs.form.get("action")print("提取的url:",url)#4、查找所有的input标签data={}for input in bs.find_all("input"):data[input.get("name")]=input.get("value")return url,data
#3、开户接口 测试def test03_trust(self):# 调用开户接口try:r = self.approve.api_trust()log.info("接口执行结果为:{}".format(r.json()))# 断言self.assertIn("form", r.text)print("请求后台开户结果为:", r.json())log.info("断言通过!")#三方开户result = parser_html(r)#期望 http://xxx,{"version":10}r=self.session.post(url=result[0],data=result[1])print("三方开户的结果为:",r.text)self.assertIn("OK",r.text)log.info("接口执行结果为:{}".format(r.text))except Exception as e:# 日志log.error("断言错误!原因:{}".format(e))# 抛异常raise
认证开户参数化
- 参数化文件(approve_trust.json)
{"img_code": [ {"desc": "获取图片验证码成功(随机小数)","random": 0.123,"expect_code": 200},{"desc": "获取图片验证码成功(随机整数)","random": 1,"expect_code": 200},{"desc": "获取图片验证码成功(随机为空)","random": " ","expect_code": 404},{"desc": "获取图片验证码成功(随机数为字符串)","random": "a123","expect_code": 400}],"recharge": [{"desc":"后台充值响应成功","valicode": 8888,"expect": "OK"},{"desc":"后台充值响应失败","valicode": 8889,"expect": "验证码错误"}]
}
- 实现难点:
1、充值需要判断验证码不同,执行步骤和结果不同
思路:
1、调用图片验证码-->记录cookie
2、调用充值接口(验证码)
3、判断验证码为8888# 1、提取响应数据# 2、三方充值# 3、断言
4、否则# 直接断言 验证码错误
#4、获取图片验证码 测试@parameterized.expand(read_json("approve_trust.json", "img_code"))def test04_img_code(self,random,expect_text):# 调用获取图片验证码接口try:r = self.approve.api_img_code(random)log.info("接口执行结果为:{}".format(r.status_code))# 断言self.assertEqual(expect_text, r.status_code)log.info("断言通过")except Exception as e:# 日志log.error("断言错误!原因:{}".format(e))# 抛异常raise@parameterized.expand(read_json("approve_trust.json", "recharge"))#5、充值接口 测试def test05_recharge(self,valicode,expect_text):# 调用充值接口try:#调用图片验证码 获取cookieself.approve.api_img_code(123)r = self.approve.api_recharge(valicode)log.info("接口执行结果为:{}".format(r.json()))if valicode==8888:# 断言self.assertIn("form", r.text)log.info("断言通过")# 三方充值result = parser_html(r)# 期望 http://xxx,{"version":10}r = self.session.post(url=result[0], data=result[1])print("三方充值的结果为:", r.text)self.assertIn(expect_text, r.text)log.info("接口执行结果为:{}".format(r.text))else:self.assertIn(expect_text,r.text)print("验证码错误响应结果为:",r.text)except Exception as e:# 日志log.error("断言错误!原因:{}".format(e))# 抛异常raise
投资模块
封装(api_tender.py)
from config import Host
class ApiTender:#初始化def __init__(self,session):#sessionself.session=session#urlself.__url_tender = Host + "/trust/trust/tender"#1、投资方法def api_tender(self,amount):#1、参数data={"id":8071,"depositCertificate":-1,"amount":amount}#2、调用请求方法return self.session.post(url=self.__url_tender,data=data)
调用(test03_tender.py)
import unittest
import requests
from util import parser_html
from api import log
from api.api_tender import ApiTender
from api.api_register_login import ApiRegisterLogin
class TestTender(unittest.TestCase):#初始化def setUp(self) -> None:#获取session对象self.session=requests.session()#获取ApiTender对象self.tender=ApiTender(self.session)#调用登录ApiRegisterLogin(self.session).api_login()#结束def tearDown(self) -> None:self.session.close()#测试方法def test01_tender(self,amount=100,expect_text="OK"):try:#调用投资方法r=self.tender.api_tender(amount)#调用三方投资result = parser_html(r)# 期望 http://xxx,{"version":10}r = self.session.post(url=result[0], data=result[1])print("三方投资的结果为:", r.text)self.assertIn("OK", r.text)log.info("接口执行结果为:{}".format(r.text))#断言self.assertIn(expect_text,r.text)except Exception as e:# 日志log.error(e)# 抛异常raise
参数化
- 文件(tender.json)
{"tender": [{"desc": "请求后台投资响应成功","amount": 100,"expect_text": "OK"},{"desc": "请求后台投资响应失败(金额为空)","amount": " ","expect_text": "不是正确的金额"}]
}
- 调用(test03_tender.py)
import unittest
import requests
from util import parser_html
from api import log
from api.api_tender import ApiTender
from api.api_register_login import ApiRegisterLogin
from util import read_json
from parameterized import parameterized
class TestTender(unittest.TestCase):#初始化def setUp(self) -> None:#获取session对象self.session=requests.session()#获取ApiTender对象self.tender=ApiTender(self.session)#调用登录ApiRegisterLogin(self.session).api_login()#结束def tearDown(self) -> None:self.session.close()#测试方法@parameterized.expand(read_json("tender.json", "tender"))def test01_tender(self,amount,expect_text):try:#调用投资方法r=self.tender.api_tender(amount)if amount==100:#调用三方投资result = parser_html(r)# 期望 http://xxx,{"version":10}r = self.session.post(url=result[0], data=result[1])print("三方投资的结果为:", r.text)self.assertIn("OK", r.text)log.info("接口执行结果为:{}".format(r.text))#断言self.assertIn(expect_text,r.text)else:self.assertIn(expect_text, r.text)except Exception as e:# 日志log.error(e)# 抛异常raise
投资业务
import unittest,requests
from util import parser_html
from api import log
from api.api_tender import ApiTender
from api.api_register_login import ApiRegisterLogin
from api.api_approve_trust import ApiApproveTrustclass TestTenderList(unittest.TestCase):def setUp(self) -> None:#获取session对象self.session = requests.session()#获取ApiRegisterLogin对象self.reg = ApiRegisterLogin(self.session)#获取ApiApproveTrust对象self.approve = ApiApproveTrust(self.session)#获取ApiTender对象self.tender = ApiTender(self.session)def tearDown(self) -> None:#关闭sessionself.session.close()def test01_tender_list(self):phone="18822228888"img_code=8888pwd="test123"phone_code=666666card_id="120101200007288410"#1、获取图片验证码self.reg.api_img_code(123123)#2、获取短信验证码self.reg.api_phone_code(phone,img_code)#3、注册self.reg.api_register(phone,pwd,img_code,phone_code)#4、登录self.reg.api_login(phone,pwd)#5、认证self.approve.api_approve(card_id)#6、后台开户r = self.approve.api_trust()#7、三方开户result = parser_html(r)r = self.session.post(url=result[0], data=result[1])print("三方开户的结果为:", r.text)self.assertIn("OK", r.text)log.info("接口执行结果为:{}".format(r.text))#8、获取充值验证码self.approve.api_img_code(123123)#9、后台充值r= self.approve.api_recharge(img_code)#10、三方充值result = parser_html(r)r = self.session.post(url=result[0], data=result[1])print("三方充值的结果为:", r.text)self.assertIn("OK", r.text)log.info("接口执行结果为:{}".format(r.text))#11、后台投资r=self.tender.api_tender(100)#12、三方投资result = parser_html(r)r = self.session.post(url=result[0], data=result[1])print("三方投资的结果为:", r.text)self.assertIn("OK", r.text)log.info("接口执行结果为:{}".format(r.text))
连接数据库工具封装
- util.py
#连接数据库工具
def conn_pymsql(sql):conn= Nonecursor=Nonetry:#1、获取连接对象conn=pymysql.connect(host="121.43.169.97",user="root",password="123456",database="czbk_member",port=3306,charset="utf8",autocommit=True)#2、获取游标对象cursor=conn.cursor()#3、执行sql语句cursor.execute(sql)#判断sql语句是否为查询if sql.split()[0].lower()=="select":#返回所有结果return cursor.fetchall()#否则else:#返回受影响的行数return "受影响的行数为:{}".format(cursor.rowcount)except Exception as e:GetLog.get_log().error(e)raisefinally:#4、关闭游标cursor.close()#5、关闭连接conn.close()
清除测试数据应用
- util.py
def clear_data():sql1="""delete i.* from mb_member_info i INNER JOIN mb_member m on
i.member_id=m.id where m.phone in
("16612345671","16612345672","16612345673","16612345674");
"""conn_pymsql(sql1)sql2 = """delete l.* from mb_member_login_log l INNER JOIN mb_member m on
l.member_id=m.id where m.phone in
("16612345671","16612345672","16612345673","16612345674");"""conn_pymsql(sql2)sql3= """delete from mb_member_register_log where phone in
("16612345671","16612345672","16612345673","16612345674");"""conn_pymsql(sql3)sql4 = """delete from mb_member where phone in
("16612345671","16612345672","16612345673","16612345674");"""conn_pymsql(sql4)
- 调用(test01_register_login.py)
@classmethoddef setUpClass(cls) -> None:#清除数据clear_data()#提示:必须在test01_xxx.py中类方法调用
运行
- 测试报告
- run_suite.py
"""
报告:htmltestreport
"""
#1、导包
from htmltestreport import HTMLTestReport
import unittest
import os
from config import DIR_PATH
#组合测试套件
suite=unittest.defaultTestLoader.discover("./script")
#指定测试报告存储目录
report_path=DIR_PATH+os.sep+"report"+os.sep+"p2p.html"
#执行测试套件
HTMLTestReport(report_path,title="p2p接口自动化测试报告").run(suite)
相关文章:

python实现接口自动化
代码实现自动化相关理论 代码编写脚本和工具实现脚本区别是啥? 代码: 优点:代码灵活方便缺点:学习成本高 工具: 优点:易上手缺点:灵活度低,有局限性。 总结: 功能脚本:工…...

如何提问 如何回答
如何提问 如何回答 如何提出一个好问题 1、选择合适的平台或专家:选择合适的论坛,或者能够给你答案的地方或者人 2、清晰描述问题背景:描述问题的背景,清楚明确地表达你的问题以及需求,不需要加过多的语气助词&#…...

计算机图形学入门26:高级光线传播
1.有偏与无偏 在做光线追踪很多方法都是用蒙特卡洛积分去估计,蒙特卡洛积分有些是无偏的(Unbiased),所谓无偏估计就是无论使用多少个样品,所估计的期望值都是正确的。那么,所有其他情况都是有偏的(Biased),就是估计的期…...

STM32蓝牙HID实战:打造低功耗、高性能的客制化键盘
一、项目概述 本项目旨在使用STM32单片机打造一款功能强大的蓝牙客制化键盘,它拥有以下特点: 九键布局,小巧便携: 满足日常使用需求,方便携带。全键可编程: 所有按键和旋钮均可通过电脑软件自定义快捷键,实现个性化功…...

C++ STL容器:序列式容器-队queue,deque
摘要: CC STL(Standard Template Library,标准模板库)在C编程中的重要性不容忽视,STL提供了一系列容器、迭代器、算法和函数对象,这些组件极大地提高了C程序的开发效率和代码质量。 STL 容器 分为 2 大类 …...

简谈设计模式之单例模式
上一篇博客已经介绍了设计模式及其设计原则, 在这篇博客中笔者会介绍一下单例模式, 也是最简单的一种设计模式 单例模式 单例模式属于创建型模式. 它涉及到一个单一的类, 该类负责创建自己的对象, 同时确保只有单个对象被创建, 这个类提供了一种访问其唯一对象的方式, 可以直…...

在Spring Boot中实现多线程任务调度
在Spring Boot中实现多线程任务调度 大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 1. Spring Boot中的任务调度 Spring Boot通过集成Spring框架的Task Execution和Scheduling支持,提供…...

dify/api/models/account.py文件中的数据表
源码位置:dify\api\models\account.py accounts 表结构 字段英文名数据类型字段中文名字备注idStringUUIDIDnameString名称emailString邮箱passwordString密码password_saltString密码盐avatarString头像interface_languageString界面语言interface_themeString界…...

SQLAlchemy迁移数据库
SQLAlchemy迁移数据库 目录 SQLAlchemy迁移数据库安装Alembic配置Alembic编辑 alembic.ini编辑env.py生成迁移文件建表语句示例修改迁移文件命名格式 安装Alembic pip install alembic配置Alembic 执行初始化后会创建一个 alembic 目录,包含Alembic的配置文件 ale…...

Django文档简化版——Django快速入门——创建一个基本的投票应用程序
Django快速入门——创建一个基本的投票应用程序 准备工作1、创建虚拟环境2、安装django 1、请求和响应(1)创建项目(2)用于开发的简易服务器(3)创建投票应用(4)编写第一个视图1、编写…...

安全防御第三天(笔记持续更新)
1.接口类型以及作用 接口 --- 物理接口 三层口 --- 可以配置IP地址的接口 二层口 普通二层口 接口对 --- “透明网线” --- 可以将一个或者两个接口配置成为接口对,则 数据从一个接口进,将不需要查看MAC地址表,直接从另一个接口出;…...

【12321骚扰电话举报受理中心-短信验证安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...

杂项——循迹模块调节方法
1-4 路灰度传感器的调节方法: 调节时探头应对着颜色较浅的上方(如果是黑白线赛道则应该将探头 对着白色上方调),轻轻的将全部可调电阻顺时针拧到底,再逆时针 慢慢回旋,直到对应探头的信号指示灯亮起后&…...

揭秘:源代码防泄密的终极秘籍
在当今信息科技高度发达的时代,源代码作为企业最核心的资产之一,其安全性不言而喻。源代码的泄露可能导致企业技术机密被竞争对手获取,进而威胁到企业的市场竞争力和长远发展。因此,源代码防泄密成为了企业信息安全工作的重中之重…...

avcodec_send_packet函数阻塞
用ffmpeg4.1.4开发一个播放器,解码过程如下,在每个函数前设置标志,测试发现程序阻塞在avcodec_send_packet函数。 while(true){av_read_frameavcodec_send_packetavcodec_receive_frameav_packet_unref } 解释如下: avcodec_se…...

一个parquet-go例子
一个parquet-go例子 使用go读写parquet,使用到了框架github.com/xitongsys/parquet-go 代码: package mainimport ("log""time""github.com/xitongsys/parquet-go-source/local""github.com/xitongsys/parquet-go/parquet&qu…...

扩散模型笔记
长参数“T”决定了生成全噪声图像所需的步长。在本文中,该参数被设置为1000,这可能显得很大。我们真的需要为数据集中的每个原始图像创建1000个噪声图像吗?马尔可夫链方面被证明有助于解决这个问题。由于我们只需要上一步的图像来预测下一步,…...

上海-LM科技(面经)
上海-LM科技 hr电话面 个人简介 个人信息的询问 是否知道芋道框架 技术面 算法题 14. 最长公共前缀(写出来即可) 聊一下Docker Docker核心概念总结Docker实战 聊一下AOP Spring AOP详解 聊一下JWT JWT 基础概念详解JWT 身份认证优缺点分析 Spring…...

用 Echarts 画折线图
https://andi.cn/page/621503.html...

C++的map / multimap容器
一、介绍 在C的map / multimap容器中,所有的元素均是pair类型(有关pair类型可以参考我之前写的 《C的set / multiset容器》的3.2中有介绍到)。 每对pair的第一个元素被称为关键字key,第二个元素被称为值value。因此,ma…...

双向链表 -- 详细理解和实现
欢迎光顾我的homepage 前言 双向链表是一种带头双向循环的链表。在双向链表中,首先存在着一个头结点;其次每个节点有指向下一个节点的指针next 和指向上一个节点的指针prev ;…...

WebGIS面试题
文章目录 1. 前端1.1. 选择器的优先级1.2. CSS 中它的布局有哪些?1.3. CSS3 的新特性1.4. CSS 的两种盒子模型1.5. CSS 的伪元素选择器和伪类选择器有哪些?1.6. ES6 的新特性1.7. 谈谈你对 promise 的理解1.8. 简单说一下原型链1.9. 简单说一下深浅拷贝1…...

代码随想录算法训练营:21/60
非科班学习算法day21 | LeetCode669:修剪二叉搜索树 ,Leetcode108:将有序数组转换为二叉搜索树 ,Leetcode538:把二叉搜索树转换为累加树 介绍 包含LC的两道题目,还有相应概念的补充。 相关图解和更多版本: 代码随想录 (progra…...

数据结构——二叉树之c语言实现堆与堆排序
目录 前言: 1.二叉树的概念及结构 1.1 特殊的二叉树 1.2 二叉树的存储结构 1.顺序存储 2.链式存储 2. 二叉树的顺序结构及实现 2.1 堆的概念 编辑 2.2 堆的创建 3.堆的实现 3.1 堆的初始化和销毁 初始化: 销毁: 插入&…...

#数据结构 链表
单向链表 1. 概念 单向链表 单向循环链表 双向链表 双向循环链表 解决:长度固定的问题,插入和删除麻烦的问题 1、逻辑结构: 线性结构 2、存储结构: 链式存储 链表就是将 结点 用链串起来的线性表,链就是 结点 中的…...

单片机软件架构连载(4)-结构体
枚举、指针、结构体,我愿称为C语言"三板斧"。 用人话来讲,几乎所有c语言高阶编程,都离不开这这3个知识点的应用。 今天站在实际产品常用的角度,给大家讲一下结构体。 1.结构体概念 结构体可以用来构建更复杂的数据结…...

工厂方法模式在金融业务中的应用及其框架实现
引言 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但由子类决定实例化哪一个类。工厂方法模式使得类的实例化延迟到子类。在金融业务中,工厂方法模式可以用于创建不同类型…...

python库(6):Pygments库
1 Pygments介绍 在软件开发和文档编写中,代码的可读性是至关重要的一环。无论是在博客文章、技术文档还是教程中,通过代码高亮可以使程序代码更加清晰和易于理解。而在Python世界中,Pygments库就是这样一个强大的工具,它能够将各…...

金斗云 HKMP智慧商业软件 任意用户创建漏洞复现
0x01 产品简介 金斗云智慧商业软件是一款功能强大、易于使用的智慧管理系统,通过智能化的管理工具,帮助企业实现高效经营、优化流程、降低成本,并提升客户体验。无论是珠宝门店、4S店还是其他零售、服务行业,金斗云都能提供量身定制的解决方案,助力企业实现数字化转型和智…...

前端JS特效第24集:jquery css3实现瀑布流照片墙特效
jquery css3实现瀑布流照片墙特效,先来看看效果: 部分核心的代码如下(全部代码在文章末尾): <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8" /> <title>jquerycss3实现瀑…...