项目地址:https://gitee.com/BanZhuanKeOrz/pytest-apitest?_from=gitee_search
Windows:
1.安装python,python版本推荐小于3.10,安装requirements.txt 的库,pycharm会提示,然后安装即可,也可以通过pip安装,版本最好一致,不同版本可能不兼容
2.配置allure,建议使用框架中的2.17.3版本,将“allure-2.17.3\bin”配置为环境变量,配置后cmd控制台使用“allure --version”验证是否安装成功
3.支持pytest.ini中文需要修改“F:\ShunYunAutoTest\venv\lib\site-packages\iniconfig\init.py”里面52的为:
# f = open(self.path)
f = open(self.path, encoding="utf-8")
Linux:
参考文件:centos7.6+python3+allure+selenium+jdk1.8安装配置.md
网页版:https://blog.csdn.net/weixin_45292690/article/details/130508470?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22130508470%22%2C%22source%22%3A%22weixin_45292690%22%7D
或者网上查下
安装python,python版本推荐小于3.10,安装requirements.txt 的库,python教程参考
配置allure,建议使用框架中的2.17.3tgz版本,将“allure-2.17.3\bin”配置为环境变量,安装参考
jdk安装,建议1.8版本,安装参考
pytest-html 生成简易报告
pytest-xdist 多线程
pytest-orderding 控制测试用例的执行顺序
pytest-rerunfailures 失败用例重跑
pytest-base-url 基础路径的配置
allure-pytest 生成allure报告
在用例上面加上:
@pytest.mark.smoke
@pytest.mark.usermanage
addopts = -v -s -m=smoke -p no:warnings --alluredir=./temps --clean-alluredir
在执行时需要在ini配置文件中加上:
-m 分组名 or 分组名
在执行时需要使用:
-m 分组名 or 分组名
@pytest.mark.run(order=1)
在所有类,所有用例之前或之后
def setup_class(self):
print("在类之前的操作")
def teardown_class(self):
print("在类之后的操作")
def setup(self):
print("在所有用例之前的前置操作")
def teardown(self):
print("在所有用例之后的后置操作")
希望在部分用例之前或之后执行。使用Fixture
Fixture装饰器完整结构如下:
@pytest.fixture(scope="作用域",autouser="自动执行",params="数据驱动",ids="参数别名",name="fixture别名")
@pytest.fixture(scope="function",params=return_list())
def mysql_test(request):
"""
这个就放着,当参考例子的
这里的params用于传输数据(list,tuple,字典列表,字典元祖),需要在夹
具里面通过request(固定写法)接收,然后通过request.param(固定写法)
获取数据,然后再通过yield把数据返回到测试用例中,然后使用。
:param request:
"""
print(request.param)
print("连接数据库")
yield request.param
print("关闭数据库连接")
应用:
1.scope:标记fixture的作用域
function:函数级别(可以手动,也可以自动)
class:类级别(一般是自动)
module:模块级别(一般是自动)
package/session:会话级别(一般是自动)
应用:
1.scope:标记fixture的作用域
function:函数级别(可以手动,也可以自动)
class:类级别(一般是自动)
module:模块级别(一般是自动)
package/session:会话级别(一般是自动)
例子:
@allure.severity(allure.severity_level.BLOCKER)
blocker:中断缺陷:致命bug:内存泄漏,用户数据丢失,系统奔溃。
critical:临界缺陷:严重bug:功能未实现,功能错误,重复提交
normal:一般缺陷,一般bug,条件查询有误,大数据了无响应等
minor级别:次要缺陷:提示bug,颜色搭配不好,字体排列不整齐,错别字。
trivial级别:轻微缺陷:轻微bug,没有使用专业术语,必填项无提示。建议。
因为pycharm自带容器。tomcat,Nginx,weblogic
1.在本地搭建本地服务器。
2.通过启动服务打开allure报告。
allure open ./reports/allures
如果是get请求,通过params传参
如果是post请求
传json格式:通过json关键字传参
传表单格式:通过data关键字传参
传文件格式:通过files关键词传参,路径需要"\"进行转义,如:
files:
file_path: “D:\test.jpg”
request:
method: post
url: /api/system/enterprise
headers: {"Content-Type": "application/json;charset=utf-8","Authorization": "${get_extract_data(cs_token)}"}
json:
userName: $csv{userName}
leader: "测试"
phone: $csv{phone}
password: "admin123"
passwordConfirm: "admin123"
enterpriseName: $csv{enterpriseName}
regionName: "华南"
address: "广东省中山市专治跌打损伤"
logo: null
nickName: "测试"
phonenumber: $csv{phone}
lat: 22.267916
lon: 113.485942
region: "2"
method: "post"
sysUser:
nickName: "测试"
phonenumber: $csv{phone}
userName: $csv{userName}
password: ""
passwordConfirm: "admin123"
request:
method: post
url: /adminManage/list
headers: {"Content-Type": "application/json;charset=utf-8","Authorization": "${get_extract_data(login_token)}"}
json: {"name":"","account":"","groupName":"","pageNo":1,"pageSize":15}
extract:
user_Id795: '"userId":(.*?),'
user_Id796: $..userId
{
"code": 0,
"message": "success",
"data": {
"name": "超级管理员",
"account": "ceshi1",
"type": 0,
"company": "绍兴处置中心",
"mobile": "133333",
"code": "ABCDEF",
"token": "hkk-F4fkghsN",
"groupId": "1,5,2,6,9,10,11,7,3,4,8",
"jurisdiction": {
"name": "超级管理员",
"id": 1
},
"areaCode": 330600,
"longitude": "120.65391",
"latitude": "30.21213"
}
}
提取变量到extract.yaml文件中:
jsonpath提取比如:
extract:
login_message: $.message
login_data_name: $..name
正则提取比如:
extract:
login_token: '"token":"(.*?)"'
一起用:
extract:
login_message: $.message
login_data_name: $..name
login_token: '"token":"(.*?)"'
从extract.yaml 文件中取值变量
如:
login_token: "${get_extract_data(login_token)}"
注意:定义的方法必须有参数;传参的时候,需要什么类型的时候,需要进行强转。int(min_number), int(max_number)
如:
##获取随机数
def get_random_number(self, min_number, max_number):
return random.randint(int(min_number), int(max_number))
yaml中调用的写法:
password: ${get_random_number(100000,999999)}
分别为:contains、equals、time、jsonpath,除了jsonpath方式,其他的当值为纯数字的时候需要加英文状态的引号。
contains:用于包含情况断言
equals:完全相等情况断言。且其中包含响应状态码断言,使用方式为:{status_code: 200}
time: 响应时间断言,填入时间即可,单位为ms,无需加单引号
jsonpath:1.path为提取实际响应内容的值 2.type为in的时候是期望值在实际值中,type为"=="的时候是期望值完全等于实际值 3.value 为期望值;jsonpath 已经支持热加载,可以直接调用方法
如:
validate:
- equals: {status_code: 200} # 状态码断言
- equals: {message: success} # 响应内容完成相等的断言
- equals: {code: 0} # 响应内容完成相等的断言
- contains: token # 包含断言
- contains: "'code': 500" # 包含断言
- contains: '500'
- time: 500 #大于500ms,断言失败
- jsonpath:
path: $.data.user.providerId #提取实际响应内容的值
type: in #断言方式,包含
type: == #断言方式,完全相等
value: 59 #期望值
value: "${get_api_info(provider_id)}" # 热加载
如:
yaml文件中:
parameters:
name-account-password-assert_str: /handle/login/login.csv parameters:
name: $csv{name}
json:
account: $csv{account}
password: $csv{password}
csv文件中:
name,account,password,assert_str
处置平台输入正确的账号密码登录,ceshi1,123456,token
account必填项检查,"",123456,账号不能为空
password必填项检查,ceshi1,"",密码不能为空
测试登录输入错误密码,ceshi1,cuowudemima,账号或密码错误
注意事项:
csv文件中写入规范:当测试输入空的时候使用两个英文状态的双引号,请勿使用中文状态的,中文的算作输入了双引号的值了
断言只能使用contains断言,可以多个的
@allure.epic("项目名称:云服务接口自动化测试项目")
@allure.feature("模块名称:个人信息相关模块")
class TestEmployeeManagement:
@allure.story("接口名称:获取个人信息信息")
@pytest.mark.parametrize("args", YamlUtil.read_testcase_yaml("/cs_imotorlinx/mine/test_employee_management/get_employee_info.yaml"))
def test_get_employee_info(self, args):
allure.dynamic.title("测试用例标题:{}".format(args['name']))
RequestUtil().analysis_yaml(args)
assert_sql 关键字不管是否使用都需要在的
sql_statement:放sql语句,因为是用于断言的,所以只支持查询的,返回的内容为,格式为字符串:
[{'user_id': 342, 'enterprise_id': 170, 'provider_id': None, 'dept_id': None, 'user_name': 'siyueqiye', 'nick_name': 'siyueqiye', 'user_type': 2, 'email': None, 'phonenumber': '177*******', 'sex': '0', 'avatar': None, 'password': '$2a$10$jVJurAhzWibCY4pEEUMDvOKjn6VJwiVpJ46nk.bIm4WngTCA93fvm', 'salt': None, 'status': '0', 'del_flag': '0', 'del_version': 0, 'login_ip': None, 'login_date': None, 'manager_id': None, 'create_by': 'admin', 'create_time': datetime.datetime(2022, 5, 6, 11, 5, 15), 'update_by': None, 'update_time': datetime.datetime(2022, 5, 17, 10, 35, 36), 'remark': None}]
yaml文件的写法如下
assert_sql:
- sql_statement: SELECT * FROM `imotor`.`sys_user` WHERE `phonenumber` LIKE '%177*******%' LIMIT 0, 1000
- sql_assert: "'user_id': 342, 'enterprise_id': 170"
- sql_reserve: "'user_id': 342, 'enterprise_id': 170"
- state: all
- switch: True
sql_assert 中是预期结果是否在实际结果里面,目前的写法格式必须为:“‘字段1’:‘字段value’”
sql_reserve 中是预期结果是否不在实际结果,一般用于硬删除的时候,目前的写法格式必须为:“‘字段1’:‘字段value’”
注意:
输入都需要英文状态的
非数字需要用单引号、数字的时候不要单引号、sql里面的内容必须是连着的
数据库某一个字段的内容为“null”的时候,python返回的时候是“None”,填写预期结果需注意
当查询出的内容没有内容的时候,python返回的是“()”,所以预期结果就填写“()”
state字段不存在的时候或者state的值为“all”的时候查询全部的数据,其他情况都是查询单条
switch 为 True 执行数据库判断,False表示不进行数据库断言
- name: 云服务获取个人信息
case_common:
allureEpic: 云服务平台接口
allureFeature: 个人信息模块
allureStory: 获取个人信息接口
base_url: ${get_base_url(url_cs_imotorlinx)}
request:
method: get
url: /api/system/user/list?pageNum=1&pageSize=1000
headers: {"Content-Type": "application/json;charset=utf-8","Authorization": "${get_extract_data(cs_token)}"}
validate:
- contains: 操作成功
assert_sql :
- sql_statement: SELECT * FROM `imotor`.`sys_user` WHERE `phonenumber` LIKE '%177*******%' LIMIT 0, 1000
- sql: "'user_id': 342"
- name: 云服务获取个人信息,错误的url
base_url: ${get_base_url(url_cs_imotorlinx)}
request:
method: get
url: /api/system/user/list1?pageNum=1&pageSize=1000
headers: {"Content-Type": "application/json;charset=utf-8","Authorization": "${get_extract_data(cs_token)}"}
validate:
- contains: "500"
assert_sql:
- sql_statement: SELECT * FROM `imotor`.`sys_user` WHERE `phonenumber` LIKE '%177*******%' LIMIT 0, 1000
- sql: "'user_id': 342"
- sql: "'enterprise_id': 17"
- state: one
加上case_common(必填),当一个yaml有多个文件的时候,在第一个用例中填写:
case_common:
# 测试项目名称
allureEpic: 云服务平台接口
# 接口相关模块
allureFeature: 个人信息模块
# 接口名称
allureStory: 获取个人信息接口
在用例文件中增加一级关键字"wait_time",会在断言之前增加等待时间(单位:秒),防止响应过慢接口导致接口失败,使用方法如下:
- name: 测试用例名称001
wait_time: 1
在用例文件增加一级关键字“is_run”,当值为“False”的时候,跳过该用例,没该关键字或其他值的时候,跳过该测试用例
- name: 获取24小时高报设备台数(/imotor/device/apumStatisticsTwentyFour)--用例编号812
#是否跳过测试用例
is_run:
is_run: False
钉钉机器人配置:
前置操作:用于当前测试用例执行之前的操作,比如通过sql将需要的值写入extract.yaml文件中,或者一些其他的前置操作,需要关键字“setup”,然后去调debug_talk.py中的方法
后置操作:用于当前测试用例执行之前的操作,比如对用例新增、编辑的操作进行还原删除操作,需要关键字“teardown”,然后去调debug_talk.py中的方法
# 后置操作
setup:
sql_extract: ${extract_sql(SELECT id FROM `imotor`.`m_report_rule` WHERE rule_name like '%接口自动化测试%'ORDER BY id DESC LIMIT 1,sql_id_1038)}
# 后置操作
teardown:
sql_teardown: ${extract_sql(SELECT id FROM `imotor`.`m_report_rule` WHERE rule_name like '%接口自动化测试%'ORDER BY id DESC LIMIT 1,sql_id_end_1038)}
project:
name: PytestApi
author: 搬砖客
url:
url_big_background: http://8.1:8888
url_iot_platform: http://39.:8080
url_imotor_ev: http://rdc-dx.com
url_imotor_prod: https://im.cn
url_imotor_dev: https://w.cn
url_imotor: https://web-px.cn
nginx_url: /imotor-api
# ding_Webhook:钉钉机器人的webhook
ding_Webhook: https://oapi.dingtalk.com/robot/send?
# conftest中的账号密码配置
login:
username: provin
password: eL0hvAfgEIHDGJWw==
password_selenium: 2
# 日志配置
log:
user_name: xzk
file_name: test.log
when: S
interval: 1
backup: 10
console_level: INFO
file_level: DEBUG
# pattern: '%(module)s - %(funcName)s - %(lineno)d - %(asctime)s - %(name)s - %(levelname)s - %(message)s'
pattern: '%(levelname)s %(asctime)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
# 数据库配置
mysql:
host: 8.4.233
user: root
password: Imotor@2022
database: imotor
port: 3306
# 配置jenkins的ip
host:
localhost: 127.0.0.1
jenkins_host: 121.210
# 配置jenkins的port
port:
jenkins_port: 8080
localhost: 8080
# sql和dingTalk的值为True的时候表示开启数据库断言和开启钉钉通知
switch:
sql: True
dingTalk: False
# 实时更新用例内容,False时,已生成的代码不会在做变更
# 设置为True的时候,修改yaml文件的用例,代码中的内容会实时更新
real_time_update_test_cases: True
# enable为True表示生成测试报告,type为“local”表示本地windows跑,jenkins表示jenkins跑
report:
enable: True
type: local
文件路径:common/readFileUtils/caseAutomaticControl.py
执行该py文件即可,前提是data中有相应的代码
把需要登录的操作,提取全局token的时候写在这边,不要去使用yaml文件
conftest.py理解为一个专门存放fixture的配置文件。
conftest.py的优势
当多个test.py同时需要使用一个前置条件,比如登录功能,此时,登录功能不能存放在某一个py中。
conftest.py可以完美解决上面问题,能够单独对fixture进行管理。
conftest.py配置文件的注意事项:
conftest.py命名是固定的,不可改变。
pytest会默认执行conftest.py中的fixture。
conftest.py只对同个目录下的py生效。
每个目录都可以有自己的conftest.py文件,一个项目中可以拥有多个conftest.py文件。
执行测试用例时,conftest.py不用手动导入。
run_main.py 按照文件中的注释使用即可,后续会优化
window本地:report/index.html
jenkins:allure-report/index.html
1.数据库断言(已完成,可优化)
2.代码自动生成,已完成
3.钉钉或者邮件发送,已经加了钉钉通知
4.jenkins集成,在linux跑的时候这个有点问题,找找问题还得-优先级4–已完成
5.增加提取数据库返回内容到extract文件中的功能–待完成-优先级3
6.数据库断言需要支持热加载(已完成)
7.接口断言、提取增加或改为jsonpath(已完成,加了热加载)
8.录制脚本–待完成(使用mitmproxy)-优先级4
9.数据库断言改为jsonpath-优先级2
10.代码注释、编写规范 完善–进行中
11.日志优化–待完成-优先级3
12.支持swagger接口文档转成yaml用例–待完成-优先级4
13.老的接口响应断言加上热加载-优先级1
14.多线程执行(待完成,可以使用 pytest-xdist)-优先级2
15.批量修改yaml文件内容(待完成),完成后将"assert_sql" 改为"assert_sql",相关代码适配-优先级2
16.多数据库连接。思路:在每个项目中建conftest文件,进行各自的数据库连接和断开 2.各平台的接口必须写在相应的文件夹内,因为根据用例来连接数据库和断开,容易被封ip
17.自动生成代码,后面也要加开关(在run_main.py中读取配置文件)-优先级3
18.extract提取需要支持jsonpath格式—已完成