Python pytest+allure的接口自动化测试框架
程序员文章站
2023-12-31 14:49:16
简单的结构运营平台项目baseApi:封装请求接口的api类,目前只使用到一个,代码如下import requests'''封装请求接口的函数,适合于需要校验登录权限的接口,设置_headers为了减少再参数化用例的时候减少headers: xxx的步骤加上**kwargs的参数为了请求接口便于传入其他参数,例如cookies= xx, files= xx'''def send(body, headers=None, **kwargs): if headers is Non...
简单的项目结构
- 运营平台项目
- baseApi:封装请求接口的api类,目前只使用到一个,代码如下
import requests
'''封装请求接口的函数,适合于需要校验登录权限的接口,
设置_headers为了减少再参数化用例的时候减少headers: xxx的步骤
加上**kwargs的参数为了请求接口便于传入其他参数,例如cookies= xx, files= xx
'''
def send(body, headers=None, **kwargs):
if headers is None:
headers = {
"Accept-Language": "zh-CN,zh;q=0.9",
"Content-Type": "application/json"
}
return requests.request(**body, headers=headers, **kwargs)
- report:存放接口报告的文件夹
- testCase:存放测试案例
- testData:存放测试数据,yaml的数据配置文件
- testSuites:需要进行测试的类,或者说接口
文件详解
- conftest:测试案例中需要使用的前置请求比如登录,数据库连接请求等。
- testCase的conftest文件代码
import pymssql
# 登录运营平台
@pytest.fixture(scope="class") #生效级别为class,详情请参照pytest,conftest文件的使用
def login():
headers = {
"User-Agent": "Chrome/10",
"flag": "guns",
"Content-Type": "application/json"
}
body = {
"username": "rhcj-gbl",
"password": "123456",
"addressmac": "04:0E:3C:D2:D0:B7"
}
data = json.dumps(body)
response = requests.post('http://登录的url/login', data=data, headers=headers)
cookie = response.cookies
return cookie
# 建立数据库连接
@pytest.fixture(scope="class") # 生效级别为class
def db():
db = pymssql.connect("host", "dbhtnews_dev", "MqfUcGymupuTmsad", "DBHTNews_DEV")
return db
- testSuites及部分代码
- 注意,这里的request_body和cookie都是从testData里边进行读取的数据,这里只写两个参数即可,参数的值应该体现在testcase里边传过来
from baseAPI.base import send
# 获取资讯类型信息
def newsinfo_getType(request_body, cookie):
response = send(request_body, cookies=cookie)
return response
# 获取资讯子类型(等价类-科创板)
def newsinfo_getSubType(request_body, cookie):
response = send(request_body, cookies=cookie)
return response
# 资讯新建(等价类-二次编辑,科创板,无标题H5模板)
def newsinfo_add(request_body, cookie):
response = send(request_body, cookies=cookie)
return response
# 资讯查询(资讯标题查询)
def newsinfo_list_byTitle(request_body, cookie):
response = send(request_body, cookies=cookie)
return response
- testCase及部分代码
import pytest
import yaml
from testSuites import marketInfo
import allure
import json
from baseAPI.base import log
@allure.feature('市场信息菜单接口') # 用于生成测试报告的模块说明
class TestMarketInfo:
# 注意!此时yml文件中的格式必须是mapping格式,不然不能进行**解包
with open('../testData/newsinfo.yml', 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
@allure.story('新闻资讯-获取资讯类型') # 用于生成测试报告的功能说明
@pytest.mark.parametrize('body', data['newsinfo_getType'])
def test_newsinfo_getType(self, login, db, body): # login参数为conftest文件的用户登录接口
newsTypeLists = [] # 资讯类型列表
newsOpenTypeLists = [] # 打开类型列表
newsFuntypeLists = [] # 功能类型列表
newsTypeDb = [] # 数据库查询资讯类型查询结果表
openNewsTypeDb = [] # 数据库打开类型查询结果表
funTypeDb = [] # 数据库功能类型查询结果表
response = marketInfo.newsinfo_getType(body, login) # login即:send函数中cookies=login,base中提到的**kwages作用显现
resdict = json.loads(response.text)
newsTypeList = resdict['newsTypeList']
newsOpentypeList = resdict['newsOpentypeList']
newsPushtitle = resdict['newsPushtitleList'][0]['typename']
newsFuntypeList = resdict['newsFuntypeList']
for i in newsTypeList:
newsTypeLists.append(i['typename'])
for j in newsOpentypeList:
newsOpenTypeLists.append(j['typename'])
for k in newsFuntypeList:
newsFuntypeLists.append(k['typename'])
try:
# 创建数据库游标
cursor = db.cursor()
sql_newsType = 'select typename from t_news_type where deleted = 0'
cursor.execute(sql_newsType)
resultNewsType = cursor.fetchall()
for i in resultNewsType:
newsTypeDb.append(i[0])
sql_openNewsType = 'select typename from t_news_opentype where deleted = 0'
cursor.execute(sql_openNewsType)
resultOpenNewsType = cursor.fetchall()
for j in resultOpenNewsType:
openNewsTypeDb.append(j[0])
sql_newsFunType = 'select typename from t_news_funtype where deleted = 0'
cursor.execute(sql_newsFunType)
resultNewsFunType = cursor.fetchall()
for k in resultNewsFunType:
funTypeDb.append(k[0])
cursor.close()
except Exception:
raise Exception('查询失败')
pytest.assume(response.status_code == 200)
pytest.assume(newsTypeLists == newsTypeDb)
pytest.assume(newsOpenTypeLists == openNewsTypeDb)
pytest.assume(newsPushtitle == '资讯')
pytest.assume(newsFuntypeLists == funTypeDb)
@allure.story('新闻资讯-获取资讯子类型')
@pytest.mark.parametrize('body', data['newsinfo_getSubType'])
def test_newsinfo_getSubType(self, login, db, body):
subtypelists = []
subTypeDb = []
response = marketInfo.newsinfo_getSubType(body, login)
print(response.json())
resdict = json.loads(response.text)
subtypelist = resdict["subtypeList"]
for i in subtypelist:
subtypelists.append(i['typename'])
try:
cursor = db.cursor()
# 注意SqlServer的字符串要用单引号''包裹,不然会查询失败
sql_subtype = "select typename from t_news_subtype where newstypeid='D003381E-FD30-43AD-BEC7-067E6D7360BC'' \
'and deleted=0"
cursor.execute(sql_subtype)
resultSubType = cursor.fetchall()
for i in resultSubType:
subTypeDb.append(i[0])
cursor.close()
except Exception:
raise Exception('查询失败')
pytest.assume(response.status_code == 200)
pytest.assume(subtypelists == subTypeDb)
@allure.story('资讯新建接口')
@pytest.mark.parametrize('body', data['newsinfo_add'])
def test_newsinfo_add(self, login, body):
response = marketInfo.newsinfo_add(body, login)
pytest.assume(response.status_code == 200)
@allure.story('资讯查询接口')
@pytest.mark.parametrize('body', data['newsinfo_list'])
def test_newsinfo_list(self, login, body):
response = marketInfo.newsinfo_list_byTitle(body, login)
pytest.assume(response.json()['rows'][0]['title'] == '接口测试-科创板')
pytest.assume(response.status_code == 200)
- @pytest.mark.parametrize是pytest传递参数的装饰器,这里表示从data(testData/newsinfo.yml)yml文件中读取,文件结构为{key1: [{key2: value2},{key3:value3}],[{},{}]}的形式,key1:当前接口所需要的测试数据key值(我这里想在一个yml文件中配置多条接口的入参文件,所以用字典的key来区分每个case应该调用的对应参数,如果不区分也可以,但是一个接口就需要一个yml配置文件,这样未免过于繁琐,不利于维护);key2;key3就表示该接口的body,也就是接口入参了,当一条case需要执行多种参数变化时(即在一个case里边执行多个不同入参的情况),可以在入参列表(或者元组)中,添加子列表入参,该装饰器会依次把每个子列表的入参传递给接口进行执行,相当于一个循环执行一个case的过程。详细的内容可以查看pytest的官方文档。pytest官方文档
- pytest.assume()为断言方法,这里不用assert因为assert会再执行报错的时候就停止执行,而pytest.assume方法可以报错后保留报错信息,之后继续往下执行案例
- testData部分入参信息,不用管data.py文件(这里我是用于调试读取文件用的)
- 举例案例中用到的是newsinfo.yml中的入参,所以—部分参数信息,如果是不需要入参的,比如get请求不需要参数进行查询,那么不写这部分的入参即可,即json为空
# 新建资讯,获取类型
newsinfo_getType:
-
method: 'post'
url: "接口url+路径"
#这里只是查询接口,所以不需要json段的入参,这里可以不写
# 新建资讯, 获取资讯子类型
newsinfo_getSubType:
-
method: 'post'
url: '接口url+路径'
json:
newsTypeid: 'D003381E-FD30-43AD-BEC7-067E6D7360BC'
# 新建资讯(二次编辑,科创板,无标题 H5模板)
newsinfo_add:
# 二次编辑
-
method: 'post'
url: "接口url+路径"
json:
displatform: 1
content: "<p>123</p>"
ischangevido: False
ismodifyvido: False
deleted: False
endtime: "2020-06-02 16:58:32"
categoryArray:
- "3F6F6B29-C1CE-400E-B048-175D0D28AF6B"
istop: "0"
dataTime:
- "2020-06-01 16:58:32"
- "2020-06-02 16:58:32"
isdefault: False
ispush: False
newsfuntypeid: "00000000-0000-0000-0000-000000000000"
newsopentypeid: "1796B11D-4AE5-4CF3-9731-DCF4C09AF657"
newspushtitleid: "0DEF1DCA-C40B-45EC-93B9-9D43E50C4834"
newssubtypeid: "B3D96CD3-9BA7-4F8F-98A2-385A6BFDF625"
source: "xxx证券"
starttime: "2020-06-01 16:58:32"
title: "接口测试-二次编辑"
isheadlines: "0"
topstate: "0"
showdisclaimer: "0"
foothtml: "<div id=\"foothtml\" data-type=\"1\" data-url=\"\" style=\"background-color: #fff;color: #999;font-size: 16px;\"></div>"
# 科创板
-
method: 'post'
url: "接口url+路径"
json:
displatform: 2
content: "<p>这是一条接口脚本数据<br/></p>"
ischangevido: True
ismodifyvido: False
contentText: "这是一条接口脚本数据"
deleted: False
endtime: "2020-06-06 16:23:46"
categoryArray:
- "3F6F6B29-C1CE-400E-B048-175D0D28AF6B"
istop: "0"
dataTime:
- "2020-06-05 16:23:46"
- "2020-06-06 16:23:46"
isdefault: False
ispush: False
newsfuntypeid: "00000000-0000-0000-0000-000000000000"
newsopentypeid: "1796B11D-4AE5-4CF3-9731-DCF4C09AF657"
newspushtitleid: "0DEF1DCA-C40B-45EC-93B9-9D43E50C4834"
newssubtypeid: "377B3F93-E7DA-40FD-BAD2-4260826CE267"
readtotal: "10"
source: "xxx证券"
starttime: "2020-06-05 16:23:46"
title: "接口测试-科创板"
videotitle: "科创板"
isheadlines: "0"
topstate: "1"
topdataTime: ["2020-06-05 16:23:46", "2020-06-06 16:23:46"]
startdate: "2020-06-05 16:23:46"
enddate: "2020-06-06 16:23:46"
showdisclaimer: "1"
foothtml: "<div id=\"foothtml\" data-type=\"1\" data-url=\"\" style=\"background-color: #fff;color: #999;font-size: 16px;\"></div>"
#无标题H5模板
-
method: 'post'
url: "接口url+路径"
json:
displatform: 2
content: "<p>我是一个无标题H5模板啊</p>"
ischangevido: False
ismodifyvido: False
deleted: False
endtime: "2020-06-30 10:13:57"
categoryArray:
- "3F6F6B29-C1CE-400E-B048-175D0D28AF6B"
istop: "0"
dataTime:
- "2020-06-09 10:13:57"
- "2020-06-30 10:13:57"
isdefault: False
ispush: False
newsfuntypeid: "00000000-0000-0000-0000-000000000000"
newsopentypeid: "1796B11D-4AE5-4CF3-9731-DCF4C09AF657"
newspushtitleid: "0DEF1DCA-C40B-45EC-93B9-9D43E50C4834"
newssubtypeid: "214DFFD7-9683-4048-99CD-31D636542BCB"
readtotal: "10"
source: "xxx证券"
starttime: "2020-06-09 10:13:57"
title: "接口测试-无标题H5模板"
isheadlines: "0"
topstate: "0"
showdisclaimer: "0"
foothtml: "<div id=\"foothtml\" data-type=\"1\" data-url=\"http://www.baidu.com\" style=\"background-color: #9FFC13;color: #363630;font-size: 16px;\">跳转百度</div>"
# 资讯查询
newsinfo_list:
-
method: 'post'
url: '接口url+路径'
json:
limit: 20
offset: 0
sort: ""
order: ""
newsOpenType: ""
displatform: ""
newsSubType: "377B3F93-E7DA-40FD-BAD2-4260826CE267"
newsType: "D003381E-FD30-43AD-BEC7-067E6D7360BC"
newsState: []
state: ""
title: "接口测试-科创板"
modifydate: []
modifyby: ""
isheadlines: ""
topstate: ""
- 这里的数据也就是baseApi.base函数的body入参,返回值**body会对body整体进行解包,即实际传入接口的方式为:
requests.request(method="post/get/update...", url="请求的接口", json="json格式的入参"/或者data="data格式的入参",**kwargs(这个根据自己需要的其他参数进行扩展加入)
) - body格式:
method: 请求方法(post, get, update...)
url: 请求的接口url(url+接口路径)
json: # json格式的入参,根据自己接口入参的格式,有可能是data类型,
key1: value1
key2: value2
- 整个项目简单的架构就到这里,下边我们来看执行测试报告的部分,这里我们用allure的插件。
安装配置allure
- 安装allure(python)测试报告插件库:
pip install allure-pytest
- 运行生成测试结果:
pytest <测试目录> --alluredir <测试结果存放目录>
例:pytest test --alluredir report/allure_origin
注意!这个时候allure_origin里边的数据只是测试结果(json.txt文件),还不是测试报告 - 生成直观的测试报告:从官网下载allure,zip包,解压到相关目录(我这里是D:\Software\allure\lib)下,同时把加压后的bin目录添加到环境变量PATH中,就额可以用allure命令了,因为allure是java编写的,所以这里需要安装jkd环境,这里就不说怎么安装jdk了。
- 生成测试报告:allure generate <allure测试结果目录> -o <存放报录> --clean(清空旧数据)例:
allure generate report/allure_origin -o report/allure_report --clean
- 在allure_report中可以看到有个index.html的文件,用浏览器打开,或者用命令
allure open /report/allure_report
(运行后会启动一个web服务用语展示报告) - 效果示例:
- 特性场景即我们前边用@allure.teature()装饰的部分,里边各个接口的说明即@allure.story()装饰的部分
告辞!
本文地址:https://blog.csdn.net/Slmple/article/details/107641925