excel+requests管理测试用例接口自动化框架

背景:

某项目有多个接口,之前使用的unittest框架来管理测试用例,将每个接口的用例封装成一个py文件,接口有数据或者字段变动后,需要去每个py文件中找出变动的接口测试用例,维护起来不方便,为了便于接口变动后维护,使用excel来管理测试用例,接口有变动不需要修改代码,只需要维护excel即可。

思路:

为了方便维护测试用例,一个接口的测试用例使用一个excel文件来管理,每个excel文件中有两个sheet页,第一个sheet页是接口的基本信息,包括接口名称,地址和请求方式,第二个sheet页为接口的测试用例,如下图所示

第一个sheet页

第二个sheet页

接口请求的数据类型为X-WWW-FORM-URLENCODED,在测试用例中每个字段为一列,每条用例为一行,倒数第二列为预期结果,倒数第三列为该条用例的描述。

接口自动化框架结构:

common目录存放公共的方法,例如写日志,连数据库

config目录存放配置文件和读取配置文件内容的方法,cfg.ini包括发送邮件的配置信息和接口的ip和端口

data目录存放接口的测试用例

logs目录存放用例执行的日志

report目录存放测试报告

run_main.py为用例执行的入口

**源码:**api_test.py封装读取测试用例的数据,执行测试用例和校验测试结果

复制代码
#coding:utf-8

import xlrd,os
import requests
from datetime import datetime
from xlrd import xldate_as_tuple
from config import readConfig
from common.logger import Log

'''
获取测试用例data所在的目录
'''
d = os.path.dirname(__file__) #返回当前文件所在目录(common文件夹路径)
parent_path = os.path.dirname(d) #返回common的父级目录
data_path = os.path.join(parent_path,'data') #返回data所在目录
data_path1 = os.listdir(data_path) #返回data目录下所有的文件

log = Log()

def api_data():
    for filename in data_path1:
        book = xlrd.open_workbook(os.path.join(data_path,filename))

        '''
        获取excel文件中接口信息
        '''
        table = book.sheet_by_index(0) #通过索引,获取相应的列表,这里表示获取excel的第一个列表
        inf_name = table.row_values(1)[0] #返回接口名称
        inf_address = table.row_values(1)[1] #返回接口地址
        inf_mode = table.row_values(1)[2] #返回请求方式

        '''
        获取excel文件中测试用例信息
        '''
        sheet = book.sheet_by_index(1) #通过索引,获取相应的列表,这里表示获取excel的第二个列表
        nrows = sheet.nrows #获取所有行数
        filed = sheet.row_values(0)
        # print(filed)

        for i in range(1,nrows):
            d1 = {}
            for j in range(0,len(filed)-2):
                ctype = sheet.cell(i, j).ctype  # 表格的数据类型
                cell = sheet.cell_value(i, j)
                d = {}
                if ctype == 2 and cell % 1 == 0:  # 如果是整形
                    cell = int(cell)
                elif ctype == 3:
                    # 转成datetime对象
                    date = datetime(*xldate_as_tuple(cell, 0))
                    cell = date.strftime('%Y/%m/%d')
                elif ctype == 4:
                    cell = True if cell == 1 else False
                # print(cell)
                d.update({filed[j]:cell})
                # print(d)
                d1.update(d)
            # print(d1)

            '''
            获取excel文件中测试用例预期结果和描述
            '''
            a = []
            for k in range(len(filed)-2,len(filed)):
                ctype = sheet.cell(i, k).ctype  # 表格的数据类型
                cell = sheet.cell_value(i, k)
                if ctype == 2 and cell % 1 == 0:  # 如果是整形
                    cell = int(cell)
                elif ctype == 3:
                    # 转成datetime对象
                    date = datetime(*xldate_as_tuple(cell, 0))
                    cell = date.strftime('%Y/%m/%d')
                elif ctype == 4:
                    cell = True if cell == 1 else False
                a.append(cell)
            # print(a[0])
            # print(type(a[0]))

            '''
            获取cfg.ini配置文件中接口公共信息(ip和port)
            '''
            ip = readConfig.ip  # 获取配置文件中接口ip
            i_port = readConfig.i_port  # 获取配置文件中接口port
            url = "http://" + ip + ":" + i_port + inf_address
            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0",
                "X-Requested-With": "XMLHttpRequest",
                "Connection": "keep-alive"
            }
            par = d1

            '''
            判断请求方式是GET还是POST,并且判断测试用例预期结果与实际响应一致
            '''
            if inf_mode == 'GET':
                r = requests.get(url, params=par)
                result = r.json()
                # log.info("---编号%s,接口名称%s---")%(i,inf_name)
                print(inf_name, str(result).replace('None','null'), a[1])
                if str(result).replace('None','null') == a[0]:
                    log.info("pass")
                    log.info("--------")
                else:
                    log.info("false")
                    log.info("--------")
            elif inf_mode == 'POST':
                r = requests.post(url, data=par, headers=headers)
                result = r.json()
                print(inf_name, str(result).replace('None', 'null'), a[1])
                if str(result).replace('None', 'null') == a[0]:
                    print('pass')
                    print('--------')
                else:
                    print('false')
                    print('--------')


api_data()

配置文件cfg.ini(主要配置邮箱和接口ip+port等常用数据信息)

复制代码
[email]

smtp_server = smtp.163.com
port = 465
sender = xxx;psw是QQ邮箱的授权码
psw = xxx

;收件人多个时,中间用逗号隔开,如'[email protected],[email protected]'
receiver = xxx[interface]

ip = xxx
;接口ip
port = xxx
;接口端口

readConfig.py读取配置文件中数据

复制代码
# coding:utf-8
import os
import configparser

cur_path = os.path.dirname(os.path.realpath(__file__))
configPath = os.path.join(cur_path, "cfg.ini")
conf = configparser.ConfigParser()
conf.read(configPath,encoding='utf-8')


smtp_server = conf.get("email", "smtp_server")

sender = conf.get("email", "sender")

psw = conf.get("email", "psw")

receiver = conf.get("email", "receiver")

port = conf.get("email", "port")

ip = conf.get("interface","ip")

i_port = conf.get("interface","port")

优化:

部分接口访问时,响应未知用户,需要用session关联接口,先调用登录接口,把登录接口的调用封装成了一个实例方法,实现了复用,登录之后,登录接口的http响应会把session以 cookie的形式set到客户端,之后的接口都会使用此session去请求封装登录接口user_login.py

复制代码
#coding:utf-8
import requests
from common.logger import Log

class Login():
    log = Log()

    def __init__(self,s):
        self.s = s

    def login(self,code,passwd):
        url = "http://192.168.20.100:8081/backend/system/user/login"
        headers = {"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
                   "User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36",
                   "X-Requested-With":"XMLHttpRequest",
                   "Cookie":"JSESSIONID=92D7FB4C7FB917B7D2E8DC429A63443F",
                   "Connection":"keep-alive"
                  }
        d = {"code":code,"passwd":passwd}

        res = self.s.post(url,headers=headers,data=d)
        result1 = res.text #字节输出
        self.log.info(u"调用登录方法,获取结果:%s"%result1)
        return res.json()

优化api_test.py中部分代码(红色部分为优化的代码)

1.在请求接口前首先调用登录接口2.加入执行用例的编号(p),每循环一次自增1

复制代码
#coding:utf-8

import xlrd,os
import requests
from datetime import datetime
from xlrd import xldate_as_tuple
from config import readConfig
from common.logger import Log
from case.user_login import Login

'''
获取测试用例data所在的目录
'''
d = os.path.dirname(__file__) #返回当前文件所在目录(common文件夹路径)
parent_path = os.path.dirname(d) #返回common的父级目录
data_path = os.path.join(parent_path,'data') #返回data所在目录
data_path1 = os.listdir(data_path) #返回data目录下所有的文件

s = requests.session()
lon = Login(s)
log = Log()


def api_data():
    p = 1
    for filename in data_path1:
        book = xlrd.open_workbook(os.path.join(data_path,filename))

        '''
        获取excel文件中接口信息
        '''
        table = book.sheet_by_index(0) #通过索引,获取相应的列表,这里表示获取excel的第一个列表
        inf_name = table.row_values(1)[0] #返回接口名称
        inf_address = table.row_values(1)[1] #返回接口地址
        inf_mode = table.row_values(1)[2] #返回请求方式

        '''
        获取excel文件中测试用例信息
        '''
        sheet = book.sheet_by_index(1) #通过索引,获取相应的列表,这里表示获取excel的第二个列表
        nrows = sheet.nrows #获取所有行数
        filed = sheet.row_values(0)
        # print(filed)

        for i in range(1,nrows):
            d1 = {}
            for j in range(0,len(filed)-2):
                ctype = sheet.cell(i, j).ctype  # 表格的数据类型
                cell = sheet.cell_value(i, j)
                d = {}
                if ctype == 2 and cell % 1 == 0:  # 如果是整形
                    cell = int(cell)
                elif ctype == 3:
                    # 转成datetime对象
                    date = datetime(*xldate_as_tuple(cell, 0))
                    cell = date.strftime('%Y/%m/%d')
                elif ctype == 4:
                    cell = True if cell == 1 else False
                # print(cell)
                d.update({filed[j]:cell})
                # print(d)
                d1.update(d)
            # print(d1)

            '''
            获取excel文件中测试用例预期结果和描述
            '''
            a = []
            for k in range(len(filed)-2,len(filed)):
                ctype = sheet.cell(i, k).ctype  # 表格的数据类型
                cell = sheet.cell_value(i, k)
                if ctype == 2 and cell % 1 == 0:  # 如果是整形
                    cell = int(cell)
                elif ctype == 3:
                    # 转成datetime对象
                    date = datetime(*xldate_as_tuple(cell, 0))
                    cell = date.strftime('%Y/%m/%d')
                elif ctype == 4:
                    cell = True if cell == 1 else False
                a.append(cell)
            # print(a[0])
            # print(type(a[0]))

            '''
            获取cfg.ini配置文件中接口公共信息(ip和port)
            '''
            ip = readConfig.ip  # 获取配置文件中接口ip
            i_port = readConfig.i_port  # 获取配置文件中接口port
            url = "http://" + ip + ":" + i_port + inf_address
            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0",
                "X-Requested-With": "XMLHttpRequest",
                "Connection": "keep-alive"
            }
            par = d1

            '''
            判断请求方式是GET还是POST,并且判断测试用例预期结果与实际响应一致,所有接口请求前先调用登录接口
            '''
            code = "xxx"  #登录接口用户code
            passwd = "xxx" #登录接口用户passwd
            lon.login(code, passwd)

            if inf_mode == 'GET':
                r = s.get(url, params=par)
                result = r.json()
                log.info("编号:%s,接口名称:%s,测试点:%s,响应:%s"%(p,inf_name,a[1],str(result).replace('None','null')))
                # print(inf_name, str(result).replace('None','null'), a[1])
                if str(result).replace('None','null') == a[0]:
                    log.info("pass")
                    log.info("--------")
                else:
                    log.info("false")
                    log.info("--------")
            elif inf_mode == 'POST':
                r = s.post(url, data=par, headers=headers)
                result = r.json()
                log.info("编号:%s,接口名称:%s,测试点:%s,响应:%s" % (p, inf_name,a[1],str(result).replace('None', 'null')))
                # print(inf_name, str(result).replace('None', 'null'), a[1])
                if str(result).replace('None', 'null') == a[0]:
                    log.info("pass")
                    log.info("--------")
                else:
                    log.info("false")
                    log.info("--------")

            p=p+1

api_data()

执行结果:

优化二:

excel中添加结果列,将每条用例的执行结果写入excel中,因为excel版本是2007以上,采用openpyxl模块去修改excel单元格的值,执行通过用绿色字体标注pass,执行不通过的用例红色字体标注false优化api_test.py中部分代码

复制代码
#coding:utf-8

import xlrd,os
import requests
import openpyxl
from openpyxl.styles import Font
# from xlutils.copy import copy
from datetime import datetime
from xlrd import xldate_as_tuple
from config import readConfig
from common.logger import Log
from case.user_login import Login


'''
获取测试用例data所在的目录
'''
d = os.path.dirname(__file__) #返回当前文件所在目录(common文件夹路径)
parent_path = os.path.dirname(d) #返回common的父级目录
data_path = os.path.join(parent_path,'data') #返回data所在目录
data_path1 = os.listdir(data_path) #返回data目录下所有的文件

s = requests.session()
lon = Login(s)
log = Log()


def api_data():
    p = 1


    for filename in data_path1:
        book = xlrd.open_workbook(os.path.join(data_path,filename))

        '''
        使用xlwt操作excel,xlwt只支持excel2007以下版本
        '''

        # wb = copy(book)
        # ws = wb.get_sheet(1)

        '''
        使用openpyxl操作excel,openpyxl支持excel2007以上版本
        '''
        wb = openpyxl.load_workbook(os.path.join(data_path,filename))
        ws = wb.worksheets[1]
        font_green = Font(color="37b400")
        font_red = Font(color="ff0000")


        '''
        获取excel文件中接口信息
        '''
        table = book.sheet_by_index(0) #通过索引,获取相应的列表,这里表示获取excel的第一个列表
        inf_name = table.row_values(1)[0] #返回接口名称
        inf_address = table.row_values(1)[1] #返回接口地址
        inf_mode = table.row_values(1)[2] #返回请求方式

        '''
        获取excel文件中测试用例信息
        '''
        sheet = book.sheet_by_index(1) #通过索引,获取相应的列表,这里表示获取excel的第二个列表
        nrows = sheet.nrows #获取所有行数
        ncols = sheet.ncols #获取所有列数
        filed = sheet.row_values(0)
        # print(filed)

        for i in range(1,nrows):
            d1 = {}
            for j in range(0,len(filed)-3):
                ctype = sheet.cell(i, j).ctype  # 表格的数据类型
                cell = sheet.cell_value(i, j)
                d = {}
                if ctype == 2 and cell % 1 == 0:  # 如果是整形
                    cell = int(cell)
                elif ctype == 3:
                    # 转成datetime对象
                    date = datetime(*xldate_as_tuple(cell, 0))
                    cell = date.strftime('%Y/%m/%d')
                elif ctype == 4:
                    cell = True if cell == 1 else False
                # print(cell)
                d.update({filed[j]:cell})
                # print(d)
                d1.update(d)
            # print(d1)

            '''
            获取excel文件中测试用例预期结果和描述
            '''
            a = []
            for k in range(len(filed)-3,len(filed)-1):
                ctype = sheet.cell(i, k).ctype  # 表格的数据类型
                cell = sheet.cell_value(i, k)
                if ctype == 2 and cell % 1 == 0:  # 如果是整形
                    cell = int(cell)
                elif ctype == 3:
                    # 转成datetime对象
                    date = datetime(*xldate_as_tuple(cell, 0))
                    cell = date.strftime('%Y/%m/%d')
                elif ctype == 4:
                    cell = True if cell == 1 else False
                a.append(cell)
            # print(a[0])
            # print(type(a[0]))

            '''
            获取cfg.ini配置文件中接口公共信息(ip和port)
            '''
            ip = readConfig.ip  # 获取配置文件中接口ip
            i_port = readConfig.i_port  # 获取配置文件中接口port
            url = "http://" + ip + ":" + i_port + inf_address
            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0",
                "X-Requested-With": "XMLHttpRequest",
                "Connection": "keep-alive"
            }
            par = d1

            '''
            判断请求方式是GET还是POST,并且判断测试用例预期结果与实际响应一致,所有接口请求前先调用登录接口
            '''
            code = "xuxingan"
            passwd = "admin"
            lon.login(code, passwd)



            if inf_mode == 'GET':
                r = s.get(url, params=par)
                result = r.json()
                log.info("编号:%s,接口名称:%s,测试点:%s,响应:%s"%(p,inf_name,a[1],str(result).replace('None','null')))
                # print(inf_name, str(result).replace('None','null'), a[1])
                if str(result).replace('None','null') == a[0]:
                    # ws.write(i,ncols-1,'pass'.encode('utf-8'))
                    ws.cell(row=i+1,column=ncols,value='pass').font = font_green
                    log.info("pass")
                    log.info("--------")
                else:
                    # ws.write(i,ncols-1,'false'.encode('utf-8'))
                    ws.cell(row=i+1, column=ncols, value='false').font = font_red
                    log.info("false")
                    log.info("--------")
            elif inf_mode == 'POST':
                r = s.post(url, data=par, headers=headers)
                result = r.json()
                log.info("编号:%s,接口名称:%s,测试点:%s,响应:%s" % (p, inf_name,a[1],str(result).replace('None', 'null')))
                # print(inf_name, str(result).replace('None', 'null'), a[1])
                if str(result).replace('None', 'null') == a[0]:
                    # ws.write(i,ncols-1,'pass'.encode('utf-8'))
                    ws.cell(row=i+1, column=ncols, value='pass').font = font_green
                    log.info("pass")
                    log.info("--------")
                else:
                    # ws.write(i,ncols-1,'false'.encode('utf-8'))
                    ws.cell(row=i+1, column=ncols, value='false').font = font_red
                    log.info("false")
                    log.info("--------")
            wb.save(os.path.join(data_path, filename))
            p=p+1


    log.info("总计%s条用例"%p)

api_data()

执行结果

总结:

第一个版本有点粗糙,1.后续加入将每条用例的执行结果写入测试用例excel文件 2.生成自动化测试报告

Python接口自动化测试零基础入门到精通(2023最新版)

相关推荐
virelin_Y.lin25 分钟前
系统与网络安全------弹性交换网络(2)
网络·安全·web安全·链路聚合·lacp·eth-trunk
天狼122229 分钟前
第5章-1 优化服务器设置
运维·服务器·adb
浪裡遊3 小时前
Linux常用指令
linux·运维·服务器·chrome·功能测试
SugarPPig3 小时前
PowerShell 查询及刷新环境变量
服务器
EasyDSS6 小时前
视频监控从安装到优化的技术指南,视频汇聚系统EasyCVR智能安防系统构建之道
大数据·网络·网络协议·音视频
我的作业错错错6 小时前
搭建私人网站
服务器·阿里云·私人网站
rufeike6 小时前
UDP协议理解
网络·网络协议·udp
王景程6 小时前
如何测试短信接口
java·服务器·前端
江理不变情7 小时前
海思ISP调试记录
网络·接口隔离原则
微网兔子7 小时前
伺服器用什么语言开发呢?做什么用什么?
服务器·c++·后端·游戏