前言:
文章是对上一篇接口自动化做的部分优化,由于之前的代码将全部精力投放在单接口如何执行,如何读取,如何循环传参上,所以导致使用起来仍然比较重,于是就有了本次优化的文章了
相信大家如果看过之前文章的测工应该知道 *parameterized *这个装饰器的实用性,这个装饰器可以将一个列表内的所有集合生成等同于列表长度的方法个数,来实现循环传参断言,(ps:这里讲一下为什么要创建测试用例个数的方法,因为unittest库中的assertEquals方法在碰到断言不通过时,整个方法就直接return出来了,如果在一个测试方法中写for循环来进行循环测试,这种做法显然不符合要求,例如我有三条case要走,执行到第二个抛个异常,这样后面的方法无法被执行到)
如果仍然不是很理解parameterized方法的同学,我就再讲下吧
上图是一个普通的加法判断方法,有两条case,case1是判断2+3 = 5 case2是判断2+3=6
如果判断失败则抛异常,并返回结果:fail
这样该方法生成的测试方法为2个,但是按照之前的设计思路,我一个接口有多条测试用例,使用一个测试方法,则可以测试这个接口的多条用例,但是我的断言结果都是前置处理之后,将结果拿进测试方法中做比对的,(这句话的意思是,我的测试方法中全是相同的代码,唯一不同的是,调用public_api文件时不一样而已),所以我得拼命去复制相同的代码,去命名不同的测试方法,这样我当时纠结了很久,这种写法是不是太傻了,
于是在一个晚上,我想来想去(主要是有个在这边工作的同学要离开上海,临走在我这住了一晚,整完的呼噜声吵得我无法入睡),于是想到了一个办法,我决定摒弃掉parameterize装饰器,
我想到了我的第二份工作时写的接口测试代码,
# -*- coding: utf-8 -*-
import unittest
import requests
import time
import json
import random
from sql_tool import sqlconnect
import csv
url = r'C:\test_document\api_unittest\test_data'
class landlordApplyCreditToAccount(unittest.TestCase):
def setUp(self):
self.base_url = r"http://121.40.178.164:7171/settlement-center-service/api/accountTransaction/landlordApplyCreditToAccount.json"
self.headers = {"Content-type":"application/x-www-form-urlencoded"}
self.timestamp = str(int(time.time()))
def t_landlord(self,arg1,arg2,arg3):
self._user = sqlconnect.model.execQuery("SELECT id FROM member_info WHERE accountOpenId = '"+arg2+"'")
self._Amount = sqlconnect.model.execQuery("SELECT realAmount FROM tally_account_info WHERE memberInfoId = '"+self._user[0][0]+"'")
value = json.dumps({"name":arg1})
data = {
"projectNo":"ZG",
"projectOrderNo":"YJSH0000130021198038a28b50f5907bcnew4691585a3a"+str(random.randint(1000,9999))+"c"+self.timestamp,
"description":value,
"accountOpenId":arg2,
"amountABS":arg3,
}
#json_request = requests.post(url=self.base_url,headers=self.headers,data=data)
#print json_request.json()['res']['msg']
self.Amount = sqlconnect.model.execQuery("SELECT realAmount FROM tally_account_info WHERE memberInfoId = '"+self._user[0][0]+"'")
if self.Amount[0][0] < data["amountABS"]:
print "余额不足请,请提现接口无效"
else:
print self.Amount[0][0],self._Amount[0][0]
self.assertEquals(self.Amount[0][0],self._Amount[0][0] + data["amountABS"],"请求接口结果错误")
@staticmethod
def getTestFunc(arg1,arg2,arg3):
def func(self):
self.t_landlord(arg1, arg2,arg3)
return func
def tearDown(self):
pass
#sqlconnect.model.execDml("UPDATE tally_account_info SET realAmount = 1000 WHERE memberInfoId = '"+self._user[0][0]+"'")
def __generateTestCases():
arglists = []
with open(url+r'\landlordApplyCreditToAccount.csv') as f:
reader = csv.reader(f)
for i in reader:
arglists.append(i)
for args in arglists:
setattr(landlordApplyCreditToAccount,'test_landlord_%s'%(args[1]),landlordApplyCreditToAccount.getTestFunc(*args))
__generateTestCases()
if __name__ == "__main__":
unittest.main()
我这里就不做敏感处理了,这些IP估计现在都ping不通了,上面这段代码中大家可以关注两个函数,
一个闭包的静态方法,这个方法的作用执行t_landlord并返回一个函数地址
@staticmethod
def getTestFunc(arg1,arg2,arg3):
def func(self):
self.t_landlord(arg1, arg2,arg3)
return func
一个生成测试方法的函数
def __generateTestCases():
arglists = []
with open(url+r'\landlordApplyCreditToAccount.csv') as f:
reader = csv.reader(f)
for i in reader:
arglists.append(i)
for args in arglists:
setattr(landlordApplyCreditToAccount,'test_landlord_%s'%(args[1]),landlordApplyCreditToAccount.getTestFunc(*args))
如果有过基础的同学应该没问题,但是我还是简单解释一下:
上图中Test类中时什么变量都没有的,在经过setattr加工处理过后,就可以将变量name给到Test类,于是我们将Test类想做是我们被测类,name想做是类中的测试方法(其实变量和方法一样都指向一个内存地址)于是就有了这段代码:
setattr(landlordApplyCreditToAccount,'test_landlord_%s'(args[1]),landlordApplyCreditToAccount.getTestFunc(*args))
第一个参数,类名称,第二个参数类中的方法,第三个方法中具体的实现
经过这样处理之后,就可以生成多个测试方法来执行测试用例,由于上面代码时几年前的代码了,所以当然不能这样写,结合我们已实现的测试代码,我做了如下调整:
def generate_test_cases(case_url,class_name,debug=True,file_name=None):
'''文件列表'''
for root, dirs, files in os.walk(case_url):
file_list = files
'''将文件夹的列表放入列表内'''
if debug:
file_url = os.path.join(case_url, file_name)
test_data = getJsonData(file_url)
for i in range(len(test_data)):
setattr(class_name, 'test_dispatch_%s' % (file_name[:-4] + str(i)), class_name.getTestFunc(test_data[i][0]))
print('------生成结束')
else:
for file in file_list:
'''循环创建测试方法,test_dispatch_文件名,arg为文件路径'''
file_url = os.path.join(case_url,file)
test_data = getJsonData(file_url)
for i in range(len(test_data)):
setattr(class_name, 'test_dispatch_%s' % (file[:-4]+str(i)), class_name.getTestFunc(test_data[i]))
print('------生成结束')
简而言之:就是运行时动态生成被测方法,以上就是优化部分,
其他细节部分,我就不再赘述,如果有需要解释的地方可以留言,我再单独开篇文章继续详解,
代码:https://github.com/ZhangWei55kai/api_testV3.1