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


;收件人多个时,中间用逗号隔开,如'a@xx.com,b@xx.com'

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.生成自动化测试报告

最后: 下方这份完整的软件测试视频教程已经整理上传完成,需要的朋友们可以自行领取【保证100%免费】

​​​软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值