• 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

  • 相关阅读:
    CS231n笔记 Lecture 4 Introduction to Neural Networks
    CS231n笔记 Lecture 3 Loss Functions and Optimization
    CS231n笔记 Lecture 1 Introduction
    LeetCode
    Python备忘录
    Celery简介
    SaltStack error: No module named 'salt'
    IO模型
    TCP协议的三次握手和四次分手
    第一章:正则表达式
  • 原文地址:https://www.cnblogs.com/Teachertao/p/12906389.html
Copyright © 2020-2023  润新知