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

 

posted @ 2020-05-17 19:06  Test挖掘者  阅读(3926)  评论(0编辑  收藏  举报