Python+API接口测试框架设计(pytest)
1.测试框架简介
整个接口测试框架的设计图如下:

base:存放的是请求方法二次封装
common:存放的是自定义工具,操作excel,yaml文件等
data:存放的是公共动态数据,如data.xls/ bookID.md
log:存放的是Log日志信息
report:存放的是接口测试报告
testcase:存放的是接口测试案例
2、重构Requests请求,查看base/method.py文件代码
import requests
class Requests:
# def __init__(self):
# self.s = requests.Session()
def request(self,url,method='get',**kwargs):
if method == 'get':
return requests.Session().request(url=url,method=method,**kwargs)
elif method == 'post':
return requests.Session().request(url=url,method=method,**kwargs)
elif method == 'delete':
return requests.Session().request(url=url,method=method,**kwargs)
elif method == 'put':
return requests.Session().request(url=url,method=method,**kwargs)
def get(self,url,**kwargs):
return self.request(url=url,**kwargs)
def post(self,url,**kwargs):
return self.request(url=url,method='post',**kwargs)
def delete(self,url,**kwargs):
return self.request(url=url,method='delete',**kwargs)
def put(self,url,**kwargs):
return self.request(url=url,method='put',**kwargs)
3、重构公共工具类方法,参考common/helper.py文件代码
import os
import logging
import datetime
def FilePath(filePath=None,fileName=None):
"""文件的路径封装"""
return os.path.join(os.path.dirname(os.path.dirname(__file__)),filePath,fileName)
def writeBookID(content):
"""写入BookID -->取出的ID为整型需强制转换为str类型"""
# print("文件写入的时间:",datetime.datetime.now())
with open(FilePath(filePath='data',fileName='bookID.md'),'w') as fp:
fp.write(str(content))
def readBookID():
"""读取BookID"""
# print("文件读取时间:",datetime.datetime.now())
with open(FilePath(filePath='data',fileName='bookID.md')) as fp:
return fp.read()
def log(log_content):
'''日志定义级别'''
# 定义文件
logFile = logging.FileHandler(FilePath('log','logInfo.md'), 'a',encoding='utf-8')
# log格式
fmt = logging.Formatter(fmt='%(asctime)s-%(name)s-%(levelname)s-%(module)s:%(message)s')
logFile.setFormatter(fmt)
# 定义日志
logger1 = logging.Logger('logTest', level=logging.DEBUG)
logger1.addHandler(logFile)
logger1.info(log_content)
logFile.close()
参考common/OperaExcel.py 文件:
import xlrd
from TestCase.ApiLogin_Po.common.OperaYaml import OperaYaml
from TestCase.ApiLogin_Po.common.helper import *
class ExcelValues:
"""Excel表的列固定"""
CaseID = 0
Desc = 1
Url = 2
Data = 3
Method = 4
Expect = 5
@property
def GetCaseID(self):
"""返回CaseID列"""
return self.CaseID
@property
def GetDesc(self):
"""返回Desc列"""
return self.Desc
@property
def GetUrl(self):
"""返回Url列"""
return self.Url
@property
def GetData(self):
"""返回Data列"""
return self.Data
@property
def GetMethod(self):
"""返回Method列"""
return self.Method
@property
def GetExpect(self):
"""返回Expect列"""
return self.Expect
class OperationExcel(OperaYaml):
@property
def readExcel(self):
"""获取Books工作表"""
sheets = xlrd.open_workbook(FilePath(filePath='data',fileName='data1.xlsx'))
return sheets.sheet_by_index(0)
def readValues(self,rowx,colx):
"""获取工作表的行、列"""
return self.readExcel.cell_value(rowx=rowx,colx=colx)
def readCaseID(self,rowx):
"""获取工作表接口ID"""
return self.readValues(rowx,ExcelValues().GetCaseID)
def readDesc(self,rowx):
"""获取工作表接口描述"""
return self.readValues(rowx,ExcelValues().GetDesc)
def readUrl(self,rowx):
"""获取工作表接口URL"""
url = self.readValues(rowx,ExcelValues().GetUrl)
if '{bookid}' in url:
return str(url).replace('{bookid}',readBookID())
else:
return url
def readData(self,rowx):
"""获取工作表接口Data"""
return self.readValues(rowx,ExcelValues().GetData)
def readMethod(self,rowx):
"""获取工作表接口方法"""
return self.readValues(rowx,ExcelValues().GetMethod)
def readExpect(self,rowx):
"""获取工作表接口预期结果"""
return self.readValues(rowx,ExcelValues().GetExpect)
def readJsonValues(self,rowx):
"""获取book.yaml文件的data值,readData对应是yaml文件的book_002,004"""
return self.readBookYaml()[self.readData(rowx)]
if __name__ == '__main__':
obj = OperationExcel()
# print(obj.readValues(1,ExcelValues().GetExpect))
# print(obj.readExpect(4),type(obj.readExpect(4)))
参考common/OperaYaml.py文件
from TestCase.ApiLogin_Po.common.helper import *
import yaml
class OperaYaml:
def readYaml(self):
with open(FilePath('data','data.yaml'),encoding='utf-8') as fp:
return list(yaml.safe_load_all(fp))
def readBookYaml(self):
with open(FilePath(filePath='data',fileName='book.yaml'),encoding='utf-8') as fp:
return yaml.safe_load(fp)
if __name__ == '__main__':
obj = OperaYaml()
# print(type(obj.readBookYaml()['book_002']))
# for item in obj.readYaml():
# print(item)
4、参考data/book.yaml文件

参考data/data1.xlsx文件

5、参考test_authFlask.py 文件,启动flask服务
from flask import Flask, jsonify, make_response, abort, Response, request
from flask_restful import Api, Resource, reqparse
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
api = Api(app=app)
auth = HTTPBasicAuth()
# 认证通过
@auth.get_password
def get_password(username):
if username == "Admin":
return "admin"
# 认证不通过的错误信息
@auth.error_handler
def authrized():
return make_response(jsonify({'msg': '您好,请认证'}), 401)
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({"error": "请求页面不存在!"}), 404)
@app.errorhandler(405)
def not_found(error):
return make_response(jsonify({"error": "请求方式不对!"}), 405)
books = [
{'ID': 1, 'author': 'Teacher', 'name': 'Python', 'done': True},
{'ID': 2, 'author': 'Teacher', 'name': 'Selenium', 'done': True},
{'ID': 3, 'author': 'Teacher', 'name': 'Appium', 'done': False},
]
class Books(Resource):
# 鉴权认证(登录)
decorators = [auth.login_required]
# 查看全部书籍
def get(self):
return jsonify({'data': books})
# 添加部分书籍
def post(self):
if not Response.json:
abort(404)
else:
book = {
'ID': books[-1]['ID'] + 1,
'author': request.json.get('author'),
'name': request.json.get('name'),
'done': False
}
books.append(book)
return jsonify({'status': 1001, 'msg': '添加书籍成功', 'data': book})
class Book(Resource):
# 鉴权认证(登录)
decorators = [auth.login_required]
# 根据ID查询对应书籍
def get(self, book_id):
book = list(filter(lambda t: t['ID'] == book_id, books))
if len(book) == 0:
abort(404)
else:
return jsonify({'status': 1002, 'msg': 'ok', 'data': book})
# 根据ID删除对应书籍
def delete(self, book_id):
book = list(filter(lambda t: t['ID'] == book_id, books))
if len(book) == 0:
abort(404)
else:
books.remove(book[0])
return jsonify({'status': 1003, 'msg': '删除书籍成功', 'data': book})
# 根据ID更新对应书籍
def put(self, book_id):
book = list(filter(lambda t: t['ID'] == book_id, books))
if len(book) == 0:
abort(404)
elif not Response.json:
abort(404)
elif 'author' not in request.json and 'name' not in request.json:
abort(404)
elif 'done' not in request.json and type(request.json['done'] is not bool):
abort(404)
else:
book[-1]['author'] = request.json.get('author', book[-1]['author'])
book[-1]['name'] = request.json.get('name', book[-1]['name'])
book[-1]['done'] = request.json.get('done', book[-1]['done'])
return jsonify({'status': 1004, 'msg': '更新书籍成功', 'data': book})
api.add_resource(Books, '/v1/api/books')
api.add_resource(Book, '/v1/api/book/<int:book_id>')
if __name__ == "__main__":
app.run(debug=True,port=5555)
6、结合pytest框架编写测试用例
import pytest
import json
from TestCase.ApiLogin_Po.base.method import Requests
from TestCase.ApiLogin_Po.common.OperaExcel import OperationExcel
from TestCase.ApiLogin_Po.common.helper import *
from requests.auth import HTTPBasicAuth
class TestBooks:
excel = OperationExcel()
obj = Requests()
auth = HTTPBasicAuth('Admin','admin')
def test_book_001(self):
"""查询所有书籍信息"""
r = self.obj.get(url=self.excel.readUrl(1),auth=self.auth)
assert self.excel.readExpect(1) in json.dumps(r.json()['data'][0]['author'])
assert r.status_code == 200
log("查询所有书籍成功")
# print(r.json()['data'],type(r.json()['data']))
# for item in r.json()['data']:
# print(item)
def test_book_002(self):
"""添加书籍请求"""
r = self.obj.post(
url=self.excel.readUrl(2),
json=self.excel.readJsonValues(2),
auth=self.auth
)
writeBookID(r.json()['data']['ID'])
assert self.excel.readExpect(2) in json.dumps(r.json()['msg'],ensure_ascii=False)
assert r.status_code == 200
log("添加书籍成功")
# print(json.dumps(r.json(),ensure_ascii=False))
def test_book_003(self):
"""查看已添加的书籍"""
r = self.obj.get(
url=self.excel.readUrl(3),
auth=self.auth
)
# print(type(r.json()['status']))
assert self.excel.readExpect(3) == float(r.json()["status"])
assert r.status_code == 200
log("查看已添加书籍成功")
def test_book_004(self):
"""更新/修改已添加的书籍"""
r = self.obj.put(
url=self.excel.readUrl(4),
json=self.excel.readJsonValues(4),
auth=self.auth
)
# print(type(json.dumps(r.json()['msg'],ensure_ascii=False)))
assert self.excel.readExpect(4) in json.dumps(r.json()['msg'],ensure_ascii=False)
assert r.status_code == 200
log("更新/修改已添加的书籍成功")
def test_book_005(self):
"""删除已编辑的书籍"""
r = self.obj.delete(
url=self.excel.readUrl(5),
auth=self.auth
)
# print(r.json())
assert self.excel.readExpect(5) in json.dumps(r.json()['msg'], ensure_ascii=False)
assert r.status_code == 200
log("删除已编辑的书籍成功")
if __name__ == '__main__':
# BB = Book()
# print(BB.excel.readUrl(1))
# pytest.main(["-s", "-v", "test_Books.py"])
pytest.main(["-s","-v","test_Books.py","--alluredir","./report/result"])
import subprocess
subprocess.call('allure generate report/result/ -o report/html --clean',shell=True)
subprocess.call('allure open -h 127.0.0.1 -p 8088 ./report/html',shell=True)
7、打开allure框架的测试报告
前提需:allure-2.7.0添加到path,安装pip install allure-pytest

Python+API接口测试框架设计(pytest)的更多相关文章
- python+API接口测试框架设计(unittest)
1.测试框架简介 整个接口测试框架的设计图如下: basepage:存放的是公共的方法 common:存放的是自定义工具 data:存放的是公共动态数据,如BB.xls/ Id.md log:存放的是 ...
- 基于python的接口测试框架设计(三)接口测试的框架
基于python的接口测试框架设计(三)接口测试的框架 其实我这里用到的是unittest单元测试框架,,这个框架好就好在比较清楚,,setup terdown都可以处理一些初始化及完成后的工作 主要 ...
- 基于python的接口测试框架设计(二)配置一些参数及文件
基于python的接口测试框架设计(二)配置一些参数及文件 我这里需要基于我的项目配置的主要是登陆参数.以及baseURL ,把这些放在单独的文件里 毕竟导入的时候方便了一些 首先是url 图略 建 ...
- 基于python的接口测试框架设计(一)连接数据库
基于python的接口测试框架设计(一)连接数据库 首先是连接数据库的操作,最好是单独写在一个模块里, 然后便于方便的调用,基于把connection连接放在__init__()方法里 然后分别定义D ...
- 【转】基于Python的接口测试框架实例
下面小编就为大家带来一篇基于Python的接口测试框架实例.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 背景 最近公司在做消息推送,那么自然就会产生很多接口,测试 ...
- 基于Python的接口测试框架实例
文章来源:http://www.jb51.net/article/96481.htm 下面小编就为大家带来一篇基于Python的接口测试框架实例.小编觉得挺不错的,现在就分享给大家,也给大家做个参考. ...
- python+selenium之框架设计
一.自动化测试框架 1.什么是自动化测试框架 简单来说,自动化测试框架就是由一些标准,协议,规则组成,提供脚本运行的环境.自动化测试框架能够提供很多便利给用户高效完成一些事情,例如,结构清晰开发脚本, ...
- 基于Python的接口测试框架
分析 接口是基于HTTP协议的,那么说白了,就是发起HTTP请求就行了,对于Python来说比较简单.直接使用requests就可以很轻松的完成任务. 架构 整个框架是比较小的,涉及的东西也比较少,只 ...
- [转]基于Python的接口测试框架
http://blog.csdn.net/wyb199026/article/details/51485322 背景 最近公司在做消息推送,那么自然就会产生很多接口,测试的过程中需要调用接口,我就突然 ...
随机推荐
- NSight Compute 用户手册(上)
NSight Compute 用户手册(上) 非交互式配置文件活动 从NVIDIA Nsight Compute启动目标应用程序 启动NVIDIA Nsight Compute时,将出现欢迎页面.单击 ...
- C++标准模板库(STL)——queue常见用法详解
queue的定义 queue<typename> name; queue容器内元素的访问 由于队列本身就是一种先进先出的限制性数据结构,因此在STL中只能通过front()来访问队首元素, ...
- 开发掉坑(二)前端静态资源 Uncaught SyntaxError: Unexpected token <
某天,有同学反馈后台管理系统出现静态资源无法加载的问题. 复现如下: 进入首页. 点击侧边栏某个子功能,静态资源可正常访问到. 等待10分钟左右,点击侧边栏其他子功能,无法访问到静态资源. 查看控制台 ...
- 3,java数据结构和算法:约瑟夫环出队顺序, 单向环形链表的应用
什么是约瑟夫环? 就是数小孩游戏: 直接上代码: 要实现这个,只需要理清思路就好了 孩子节点: class Boy{ int no;//当前孩子的编码 Boy next; // 下一节点 public ...
- QT环境变量设置
首先找到自己对应的目录 我的如图 还有一个路径 将这两个路径添加到系统变量的Path中
- 复习Spring第一课--Spring的基本知识及使用
关于Spring: spring容器是Spring的核心,该容器负责管理spring中的java组件, ApplicationContext ctx = new ClassPathXmlApplic ...
- 【.NET 与树莓派】TM1638 模块的按键扫描
上一篇水文中,老周马马虎虎地介绍 TM1638 的数码管驱动,这个模块除了驱动 LED 数码管,还有一个功能:按键扫描.记得前面的水文中老周写过一个 16 个按键的模块.那个是我们自己写代码去完成键扫 ...
- Ubuntu18.04下安装Docker并配置SSL证书加密远程连接
Docker下载与安装 下载安装包 国内网络连接docker镜像还是比较慢的,这里推荐直接下载docker镜像,Ubuntu镜像下载路径为:https://download.docker.com/li ...
- (Element UI 组件 Table)去除单元格底部的横线
Element UI 组件 Table 有一个属性 border,添加它可以增加纵向边框,但是无法控制横线边框,因此即使是最简单的 el-table,也会包含一个底部横线. 这个底部横线其实是一个 b ...
- PUN丨实用API
原文地址:PUN丨实用API 当前房间人数 int num = PhotonNetwork.CurrentRoom.PlayerCount; 当前脚本是否属于当前玩家 //需继承MonoBehavio ...