好久不更新了,今天又要杀回来了,哈哈哈哈~~~
切入正题,最近公司要求开展自动化,于是将搁置的接口自动化重新梳理了一下,晒出来以防遗忘。
接口框架有四个包组成:
1,testdata包:
case_data.xlsx存放接口数据
使用excel读取接口数据。首页接口信息:接口名、请求url、请求方式、是否执行等。excel从第二个sheet开始每个记录的测试数据。请求参数、依赖数据、响应结果、数据存储、是否执行、执行结果、错误信息记录
2,config包:
PublicData.py存:存放路径、数据库配置、excel数据下标常量、全局变量
3,util包:
ParseExcel.py:解析excel工具类
HttpClient.py:封装了request方法,调用request方法传入参数:url,method,type,requestdata=None,header=None得到响应结果
my_rsa_encry.py:参数加密方法,对参数进行ras加密,传入公钥和要加密的数据得到加密后的密文
4,aciton包:
data_store.py存储数据:传入参数:case_name(接口名称),case_id(接口id),store_param(存储数据的结构),requestdata(接口的请求数据),responsedata(接口响应数据),方法执行后将数据存储到全局变量REQUEST_DATA和RESPONSE_DATA中,后续依赖数据就是从这里取值。
源码:
#coding=utf-8 from config.PublicData import REQUEST_DATA,RESPONSE_DATA class DataStore(): def __init__(self): pass @classmethod def store(cls,case_name,case_id,store_param,requestdata=None,responsedata=None): for k,v in store_param.items(): if k=='request': # param=data:toLoginKey、mobile、pwd for param in v: #如果保存的参数只有一个层级,直接从请求中取值 if param.find(':')==-1: value=requestdata[param] #如果保存的参数多个层级,需要拼接后再从请求中取值 else: param_list=param.split(':') value='requestdata' for index in range(len(param_list)): value+='["'+param_list[index]+'"]' value=eval(value) param=param_list[-1] if REQUEST_DATA.has_key(case_name): if REQUEST_DATA[case_name].has_key(case_id): REQUEST_DATA[case_name][case_id][param]=value else: REQUEST_DATA[case_name][case_id]={param:value} else: REQUEST_DATA[case_name]={case_id:{param:value}} elif k=='response': # param=data:toLoginKey、mobile、pwd for param in v: #如果保存的参数只有一个层级,直接从响应中取值 if param.find(':')==-1: value=responsedata[param] #如果保存的参数多个层级,需要拼接后再从响应中取值 else: param_list=param.split(':') value='responsedata' for index in range(len(param_list)): value+='["'+param_list[index]+'"]' value=eval(value) param=param_list[-1] if RESPONSE_DATA.has_key(case_name): if RESPONSE_DATA[case_name].has_key(case_id): REQUEST_DATA[case_name][case_id][param]=value else: RESPONSE_DATA[case_name][case_id]={param:value} else: RESPONSE_DATA[case_name]={case_id:{param:value}} else: print 'store_param里面的数据参数来源非请求或者响应中:store_param data not from in request or response' if __name__=='__main__': requestdata={"mobile":"1760034xxxx","pwd":"aaaa1111",'email':'xxx@xxx.com'} responsedata={u'msg': u'suc', u'params_num': None, u'code': u'0', u'params': None, u'data': {u'onlyGoogleAuth': 0, u'toLoginKey': u'OGM3YzdkNTgtMjkyZi00ODA3LTg4ZmUtMzg2YTU1NWQ0OTE5MTU4Mzc2MjYwMTIwMA%3D%3D', u'email': u'xxxn@xxx.com', u'googleAuth': 1, u'mobileAuth': 1, u'emailAuth': 0, u'showAc': 0}} store_param={"response":["data:toLoginKey"],'request':['mobile','pwd']} print REQUEST_DATA,RESPONSE_DATA DS=DataStore() for i in range(2): DS.store('login',str(i),store_param,requestdata,responsedata) print REQUEST_DATA print RESPONSE_DATA
store_param格式为:store_param={"response":["data:toLoginKey"],'request':['mobile','pwd']}
requestdata={"mobile":"1760034xxxx","pwd":"aaaa1111",'email':'xxx@xxx.com'}
responsedata={u'msg': u'suc', u'params_num': None, u'code': u'0', u'params': None, u'data': {u'onlyGoogleAuth': 0, u'toLoginKey': u'OGM3YzdkNTgtMjkyZi00ODA3LTg4ZmUtMzg2YTU1NWQ0OTE5MTU4Mzc2MjYwMTIwMA%3D%3D', u'email': u'xxxn@xxx.com', u'googleAuth': 1, u'mobileAuth': 1, u'emailAuth': 0, u'showAc': 0}}
REQUEST_DATA={'login': {'1': {'mobile': '1760034xxxx', 'pwd': 'aaaa1111'}, '0': {'mobile': '1760034xxxx', 'pwd': 'aaaa1111'}}} RESPONSE_DATA={'login': {'1': {'toLoginKey': u'OGM3YzdkNTgtMjkyZi00ODA3LTg4ZmUtMzg2YTU1NWQ0OTE5MTU4Mzc2MjYwMTIwMA%3D%3D'}, '0': {'toLoginKey': u'OGM3YzdkNTgtMjkyZi00ODA3LTg4ZmUtMzg2YTU1NWQ0OTE5MTU4Mzc2MjYwMTIwMA%3D%3D'}}}
getrely.py:组装请求数据。有些请求数据需要依赖其他接口的请求后者响应数据。这个类就是为了根据依赖关系从新组装请求数据。
源码:
#coding=utf-8 from util.my_rsa_encry import myencry from util.my_rsa_decry import mydecry class GetKey(): def __init__(self): pass #根据依赖数据的结果从RESTDATA,RESPONSEDATA里面取值放到requstdata中 def get(self,case_name,requestdata,relaydata,RESTDATA,RESPONSEDATA): for relay_param,relay_path in relaydata.items(): for requst_or_response,case_path in relay_path.items(): casename,case_id=case_path.split('->') #依赖数据中存在“:”,所以依赖的数据不是单纯根据caseid取值,是有对应关系的。 if case_id.find(":")!=-1: id,store_parem=case_id.split(':') #从存储请求数据的常量REQUEST中取值 if requst_or_response=='request': value=RESTDATA[casename][id][store_parem] requestdata[relay_param]=value #从存储响应数据的常量RESPONSE中取值 if requst_or_response=='response': value=RESPONSEDATA[casename][id][store_parem] requestdata[relay_param]=value #依赖数据中没有’:‘,依赖数据直接从datastore里面取值 else: #从存储请求数据的常量REQUEST中取值 if requst_or_response=='request': value=RESTDATA[casename][case_id][relay_param] requestdata[relay_param]=value #从存储响应数据的常量RESPONSE中取值 if requst_or_response=='response': value=RESPONSEDATA[casename][case_id][relay_param] requestdata[relay_param]=value if case_name=='login_verify': pk=requestdata['pk'] en=myencry(pk) mobile=requestdata['mobileNumber'] pwd=requestdata['loginPword'] requestdata['mobileNumber']=en.get_encry_str(mobile) requestdata['loginPword']=en.get_encry_str(pwd) return requestdata if __name__=='__main__': #存在问题:依赖的参数名和存储数据的参数名不一致,这样就无法从存储数据中找到依赖数据。 #改进:在依赖数据中加入对应关系。请求的参数对应响应的参数名 requestdata={"lang": "zh_CN", "mobileNumber":"${rsa(xxxn@cxxx.com)}" , "loginPword":"${rsa(xxxx)}" , "recaptcha": "", "pk": "", "genkey": ""} relaydata={"mobileNumber":{"request":"login->1"},'loginPword':{"request":"login->2"},"pk":{"response":"get_pk->1:data"}} RESTDATA={'login':{'1':{'mobileNumber':'xxxx','loginPword':'aaaa1111'},'2':{'mobileNumber':'xxxx','loginPword':'aaaa1111','other':"aaa"}},'register':{'1':{'name':"xxx"}}} RESPONSEDATA={'get_pk':{'1':{'data':'PK_VALUE'},'2':{'mobileNumber':'xxx','loginPword':'xxx','other':"xx"}},'register':{'1':{'name':"xxx"}}} getkey=GetKey() print getkey.get(requestdata,relaydata,RESTDATA,RESPONSEDATA) {"response":{"genkey":"login_1->3"}} {"genkey":{'response':'login_1->3'}}
check_result.py:结果校验。
源码:
#coding=utf-8 import re def check(checkpoint,responsedata,errorinfo): errorinfo={} for k,v in checkpoint.items(): #从响应中取值要匹配的值 param=''.join(map(lambda x:'[\'%s\']' %x,k.split(':'))) try: value=eval('responsedata'+param) except Exception,e: print 'responsedata not have %s' %param #检查内容需要正则匹配 if isinstance(v,dict): for type,patten in v.items(): patten=re.compile(patten) if type=='type': try: re.search(patten,value).group() except Exception,e: errorinfo[k]=value #检查内容值匹配 else: if value!=v: errorinfo[k]=value if len(errorinfo.keys())==0: return None else: return errorinfo if __name__=='__main__': a='xxx@qq.com' responsedata={u'msg': u'suc', u'params_num': None, u'code': u'0', u'params': None, u'data': {u'onlyGoogleAuth': 0, u'toLoginKey': u'OTA4Y2Q0MDktMDA5OC00NGJiLWE1YjMtMzQ5ODE4Yjg0OTlhMTU4NDAyODE5NzkzNA%3D%3D', u'email': u'xxx@qq.com', u'googleAuth': 1, u'mobileAuth': 0, u'emailAuth': 0, u'showAc': 0}} checkpoint={'data:showAc':0,'data:email':{'type':'.*@.*\.com'},'msg':'suc'} errorinfo={'msg':'aaa'} print check(checkpoint,responsedata,errorinfo)
write_result.py:将请求结果、断言结果写入excel。
源码:
from config.PublicData import * def write(excel,case_detail_sheet,case_detail_idx,responsedata,errorinfo): excel.writeCell(case_detail_sheet,str(responsedata),rowNo=case_detail_idx,colsNo=ResponseData) if errorinfo: # writeCell(self, sheet, content, coordinate = None,rowNo = None, colsNo = None, style = None): excel.writeCell(case_detail_sheet,'fail',rowNo=case_detail_idx,colsNo=Status,style='red') excel.writeCell(case_detail_sheet,str(errorinfo),rowNo=case_detail_idx,colsNo=ErrorInfo) else: excel.writeCell(case_detail_sheet,'pass',rowNo=case_detail_idx,colsNo=Status)
接口框架入口:读取excel第一页获取接口信息,根据接口名获取接口的请求数据。请求需要依赖数据则调用依赖方法将请求拼接完成。调用请求接口方法获得接口响应数据。如果需要保存数据则调用数据保存方法进行保存,最后检查结果,将接口执行的结果和错误日志写入excel
搭建框架过程中出现的问题:
1.依赖参数和存储名字不一致:比如下单接口需要传入token,token由登录接口返回。但是登录接口返回的token参数名为data。开始设置框架的时候,没考虑到这一点,取不到依赖数据。后来框架中加入了参数的对应关系:token依赖于login接口响应值中的data值
2,读取excel内容的时候。开始封装的excel方法取行和列数字的时候只是传了行列下标。这样在读取的时候如果涉及到不同sheet的数据,还要在入口方法中切换sheet,非常容易读取错误。所以excel封装了新方法,获取行列数据的时候传入sheet。这样就可以清晰的知道要取哪一个sheet中的航和列。不会因为忘记切换sheet而取错数据。
3,数据存储。 定义REQUEST和RESPONES字典类型存储依赖数据。存放在config的参数文件为全局变量。 封装数据存储的时候,应该是直接往里面放入存储的数据。比如:RQUEST[casename]={} 但是在组织数据的时候不小心直接给REQUEST赋值了(REQUEST={...});把REQUEST和RESPONES重新复制,相当于方法的私有变量,重新开启了内存空间。导致全局没有复制。后续从存储数据中取值没有取出来
不完善的地方:
1,断言函数:校验只局限于校验响应结果,没有关联数据库。比如下单接口返回与接口文档一致,但是用户账户扣款未校验。
2,执行结果只写到了excel中,应该介入报警邮件即使响应
3,excel结构:一个接口一个sheet,接口太多不好维护
以上就是整体的框架结构,附上框架源码链接: https://pan.baidu.com/s/1gXBCzUu19S1H8h1IpuHLVw 提取码: 6pfr
有兴趣的小伙伴可以继续改进,bye~~