Bootstrap

接口自动化测试框架

接口自动化测试框架

python+unittest+requests+ddt+openpyxl+pymysql+logging+unittestreport+jenkins

项目结构

|-- conf  # 配置文件层
|   |-- setting.py  # 配置文件,存放数据库连接信息、接口地址等全局配置
|-- logs  #日志管理层
|   |-- test.log  # 日志文件,记录测试过程中的详细信息
|-- test_datas  # 数据管理层
|   |-- test_data.xlsx  # Excel 文件,用于存储测试数据
|-- test_cases  # 测试用例层
|   |-- test_1_login.py  # 登陆
|   |-- test_2_upload.py  # 图片上传
|   |-- test_3_create_product.py  # 创建商品
|   |-- test_4_member_register.py  # 用户注册
|   |-- test_5_member_login.py  # 用户登陆
|   |-- test_6_place_order.py  # 用户下单
|   |-- test_7_unittest_all.py  # 集成所有用例
|-- tools  #  工具层
|   |-- handle_excel.py  # 工具类,测试数据操作封装
|   |-- handle_db.py  # 工具类,数据库操作封装,用pymysql连接mysql数据库,操作数据库等一系列方法,进行数据校验;
|   |-- handle_phone.py  # 工具类,封装一个可以通过faker库造数据生成随机手机号的方法
|   |-- handel_extract.py  # 工具类,从接口响应结果中提取全局变量用于:鉴权、参数依赖提取
|   |-- handle_replace.py  # 工具类,封装一个参数替换的工具,对请求参数进行处理,返回可以直接发送请求的参数
|   |-- handle_path.py  # 工具类,存放路径封装
|   |-- handle_requests.py  # 工具类,请求封装,兼容图片上传的接口
|   |-- handle_reponse.py  # 工具类,响应结果处理
|   |-- handle_attribute.py  # 工具类,动态参数设置成类属性
|   |-- handle_space.py  # 工具类,空格处理工具
|   |-- handle_logs.py  # 工具类,配置和获取日志记录器
|-- main.py  # 框架执行入口

conf配置文件层

#setting.py 用于存放当前框架中的所有配置信息(使用不频繁,修改不频繁的数据)
#通过配置文件传参-软编码;在代码中写死-硬编码;


#管理者登陆账号信息
user_info = {"user_name":"xdfjewg","password":"dfergbu"}

#图片上传信息
image_info = {"file_name":"song.png","file_type":"image/png"}

#连接数据库信息
mysql_info = {"host":"11.111.111.11","post":"3306","user":"lemon","password":"vdnjkvs","db":"fsegjvskjv"}

#数据库断言替换信息,如"file_path":"2022/04/cd54sdgruissafmscsd/vds"图片上传路径
assert_db_info = {}

#前置sql语句执行需要替换的参数
setup_sql_info = {}

logs日志管理层

tools工具层

  • excel数据读取
  • 数据库操作:连接数据库并查询
  • 构造测试数据
  • 响应数据提取
  • 参数替换
  • 存放路径
  • 请求封装
  • 响应结果处理
  • 空格换行符处理工具
  • 结果断言
  • 装饰器

handle_excel测试数据操作封装

excel中测试数据操作逻辑:通过第三方库openpyxl模块来操作excel中测试数据的读写,打开workbook工作薄,获取sheet表单,获取表单中所有数据,从而调用测试数据,实现数据驱动;自己二次封装一个读写excel中测试数据的工具;

from openpyxl import load_workbook


class HandleExcel:
    def __init__(self,file_name):  #file_name: 测试用例文件名称(绝对路径)
        self.wb_obj = load_workbook(filename=file_name) #load_workbook加载excel

        #获取所有excel中sheet的名称
        self.sheet_names = self.wb_obj.sheetnames

    def get_excel_test_cases(self,sheet_name):  #sheet_name: excel中sheet名称
        #临时变量存放数据
        cases_list = []  
        #获取指定表单对象
        sheet_obj = self.wb_obj[sheet_name]
        #iter_rows迭代所有行数据,按行读取封装成list输出结果类似于[('id','title'),(1,'登陆成功')],values_only是False返回对象,True返回单元格对应数据,datas获取表单sheet中所有数据
        datas = list(sheet_obj.iter_rows(values_only=True))
        #获取表头
        case_title = datas[0] 
        #获取表数据
        case_datas  = datas[1:] 
        #遍历每一行数据
        for case in case_datas:
            #zip(key,val)把两个元素压缩在一起,再把每一行数据使用dict转换成字典类型,zip函数是python的内置函数
            result = dict(zip(case_title,case))
            cases_list.append(result)
        self.close_file()
        return cases_list

    def close_file(self):
        #关闭excel
        self.wb_obj.close()  

if __name__ == '__main__':
      #存放excel测试数据的文件名(放本目录下的路径)
      cl = HandleExcel(file_name="case_data.xlsx",sheet_name="login")
      cl.get_excel_test_cases()

handle_db数据库操作

import pymysql
#从配置文件中导入数据库登陆信息
from conf.setting import mysql_info

# **mysql_info
class HandleDb:

    def __init__(self,mysql_info):
        self.db = pymysql.connect(
            host=mysql_info["host"],
            port=mysql_info["port"],
            user=mysql_info["user"],
            password=mysql_info["password"],
            db=mysql_info["db"],
            #设置自动提交
            autocommit=True,
            #DictCursor设置成字典类型,字典嵌套在list中,一行数据为一个字典;
            cursorclass=pymysql.cursors.DictCursor
        )
        #游标连接
        self.cur = self.db.cursor()


    #关闭数据库
    def db_close(self):
        #先关游标
        self.cur.close()
        #再关数据库连接
        self.db.close()

    #获取数据,sql查询到什么数据,我们就返回
    def get_datas(self,sql):
        value_list = []
        #执行sql语句
        self.cur.execute(sql)
        #获取sql查询的数据
        result = self.cur.fetchall()
        for __dict in result:
            for val in __dict.values():
                value_list.append(val)
        return value_list

    # 返回dict类型的数据
    def get_data_dict(self,sql):
        self.cur.execute(sql)
        result = self.cur.fetchall()
        return result

mysql = HandleDb(mysql_info=mysql_info)

if __name__ == '__main__':
    cl = HandleDb(mysql_info=mysql_info)
    cl.get_data_dict(sql=sql1)

handle_phone构造测试数据

from faker import Faker
from tools.handle_db import mysql

class HandlePhone:
    def __init__(self):
        self.fk = Faker(locale="zh-cn")


    def __check_phone(self,phone):
        sql = "SELECT * FROM 表名 WHERE user_mobile = '{}'".format(phone)
        #去数据库查询是否注册,注册了再重新生成,直到在数据库中找不到,就表示未注册;
        result = mysql.get_datas(sql=sql)
        return result

    def get_phone(self):
        while True:
            #生成新的手机号
            phone = self.fk.phone_number()
            # 去数据库校验是否已注册
            result = self.__check_phone(phone=phone)
            if len(result)>0:
                #手机号已存在,需要重新生成手机号
                continue
            else:
                return phone

handel_extract响应数据提取

"""
逻辑:
1、在excel中新增extract_data,用于存储提取数据的key以及提取表达式(jsonpath)
2、在请求需要鉴权的接口之前,去请求登陆接口,读取extract_data中的数据,获取字典的key(响应结果中key),values(jsonpath表达式),从响应结果中提取到鉴权信息,设置到类属性作为全局变量
3、如果是鉴权,就在请求需要鉴权的接口之前,将这个鉴权的token设置到请求头里面
4、如果是参数依赖,其他接口在发请求之前,去获取到相应的参数,替换自己的请求参数
"""

import ast
from jsonpath import jsonpath


from tools.handle_attribute import HandleAttr

class HandleExtract:
    # excel中extract_data字段的数据,response接口响应结果
    def handle_extract(self,extract_data,response):
        if extract_data:
            extract_data = extract_data if isinstance(extract_data,dict) else ast.literal_eval(extract_data)
            for key,value in extract_data.items():
                token = jsonpath(response,value)[0]
                #将提取出的token鉴权设置为全局变量
                setattr(HandleAttr,key,token)
        else:
            print("extract_data数据为空,不需要提取全局变量")

if __name__ == '__main__':
    cl = HandleExtract()
    #"$..access_token"是jsonpath提取表达式,选择所有名为access_token的属性
    extract_data = {"access_token":"$..access_token"}
    response={'access_token': 'aef4b927-ffa0-4c0c-a8a1-a844c9a7a423', 'token_type': 'bearer', 'refresh_token': '58edfd15-1f90-49f0-9094-7ad74eb72620', 'expires_in': 1295999}
    cl.handle_extract(extract_data,response)

handle_replace参数替换

 def replace_data调用方法及其顺序如下所示:

  1. def __handel_str         删除换行符和空格
  2. def __get_replace_keys    获取需要替换的参数名称
  3. def __set_attribute,其中__set_attribute调用方法及其顺序如下:

        3-1 def replace_sql,其中replace_sq调用方法及其顺

;