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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM