一个接口有两种请求方式?这个接口明显是post请求,但是上面的验证参数是个什么东东呢?在我短暂的蒙X状态过后瞟见了“token”这个名字,对,没错这其实就是一种接口的动态参数,至于后面的GET请求方式你可以把它们理解为是和接口请求URL组合在一起的(也就是说原始的URL+动态参数串才是完整的URL),这样思路一下子就清晰了,那么接下来要做的就是获取这些会动的参数了
token的验证现在应用越来越广泛,尤其是针对公司内属于主要业务的接口,它的模式是每次进行业务请求前系统需要验证你的“身份”,如上图所示这个身份就是token和appname组成的动态参数串;appname是系统分配给用户的一个唯一标识,token是通过系统分配给用户的appkey通过一系列加密方法配合时间戳生成的,每个token的有效期是10分钟并且只能使用一次,这样就要求进行接口自动化测试的人员每个脚本执行前去生成一个token
具体该怎么做呢,拿我这个项目举例:首先需要咨询开发同学token的加密方法(一般负责的RD在wiki里会有写,没有也没关系,知道加密方法后可以去网上搜索源码),我这个项目是通过AEB加密,相对应python的源码就是这个
# -*- coding: utf-8 -*- import time from Crypto.Cipher import AES from binascii import b2a_hex, a2b_hex #该模块提供生成16位动态token的AEB加密算法和解密算法 class crypt(): def __init__(self): # 当前appname对应的appKey #self.key = key # 随机向量 #self.iv = iv self.mode = AES.MODE_CBC self.BS = AES.block_size self.pad = lambda s: s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS) self.unpad = lambda s: s[0:-ord(s[-1])] def encrypt(self, text,key,iv):#加密算法,HttpUntitlsZ中需要token验证的请求方法通过调用这里生成动态token text = self.pad(text) self.obj1 = AES.new(key, self.mode, iv) self.ciphertext = self.obj1.encrypt(text) return b2a_hex(self.ciphertext) def decrypt(self, text,key,iv):#解密算法,调用逻辑同加密算法 self.obj2 = AES.new(key, self.mode, iv) plain_text = self.obj2.decrypt(a2b_hex(text)) return self.unpad(plain_text.rstrip('\0'))
当然你需要在自己的工程里导入AEB依赖的第三方库pycrypto(这个玩意儿安装起来比较费劲,具体可以度娘),把这段源码稍微改造一下放在你得工程里作为公共资源,下面我们就开始
进行接口测试的准备了,就是我上一篇博客中提到的公共资源,详细见下
# -*- coding: utf-8 -*- import os,datetime,fs_datadevices,json,httplib,logging,requests,urllib,urllib2,time,codecs from requests.models import Response from Main import Token from binascii import b2a_hex, a2b_hex from Conf import Config #该模块依赖requestes获取接口请求返回体,并处理相关异常和记录日志;实现get,post接口验证请求,包括使用cookies以及token验证 class HttpUntils: def __init__(self,url,data,headers,cookies): self.url = url self.data = data self.herders = headers self.cookies = cookies self.repeson = None conf_name = 'token.ini' self.con = Config.Config() logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='myapp.log', filemode='w') ################################################################################################# #定义一个StreamHandler,将DEBUG级别或更高的日志信息打印到标准错误,并将其添加到当前的日志处理对象# console = logging.StreamHandler() console.setLevel(logging.INFO) formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console) ################################################################################################# self.appname = {} self.key = {} self.iv = {} ConfigData = self.con.readconfig(conf_name) for i in ConfigData.keys(): self.appname[i] = ConfigData.get(i).get('appname') self.key[i] = ConfigData.get(i).get('key') self.iv[i] = ConfigData.get(i).get('iv') # 当前时间戳 self.tamp = int(time.time() * 1000) self.t = Token.crypt() def Get(self):#普通的get请求实现方法 if 'auth' in self.data.keys(): Auth = self.data.get('auth') data_p = urllib.urlencode(self.data) oldAuth = data_p.split('&')[3].split('=')[1] get_data = str(data_p).replace(oldAuth,Auth) else: get_data = urllib.urlencode(self.data) try: r = requests.get(self.url,params=get_data,verify=False) if r.status_code == 200: self.repeson = r elif r.status_code in [502,504]: self.repeson = 'Gateway error' logging.warning(self.repeson) else: self.repeson = 'sever is error' logging.warning(self.repeson) except requests.ConnectionError, e: # 处理DNS网络问题异常 print e except requests.HTTPError, h: # 处理无效http响应异常 print h except requests.Timeout, t: # 处理请求超时异常 print t return self.repeson def Get_cookies(self):#需要cookie验证的get请求实现方法 if 'auth' in self.data.keys(): Auth = self.data.get('auth') data_p = urllib.urlencode(self.data) oldAuth = data_p.split('&')[3].split('=')[1] get_data = str(data_p).replace(oldAuth,Auth) else: get_data = urllib.urlencode(self.data) try: r = requests.get(self.url,params=get_data,cookies=self.cookies,verify=False) if r.status_code == 200: self.repeson = r elif r.status_code in [502,504]: self.repeson = 'Gateway error' logging.warning(self.repeson) else: self.repeson = 'sever is error' logging.warning(self.repeson) except requests.ConnectionError, e: # 处理DNS网络问题异常 print e except requests.HTTPError, h: # 处理无效http响应异常 print h except requests.Timeout, t: # 处理请求超时异常 print t return self.repeson def Post(self):#不需要cookies的post请求方法 if 'x-www-form-urlencoded' in self.herders.get('Content-Type'): Post_data = urllib.urlencode(self.data) elif 'json' in self.herders.get('Content-Type'): Post_data = json.dumps(self.data) else: Post_data = self.data try: r = requests.post(self.url,data=Post_data,headers=self.herders,verify=False) if r.status_code == 200: self.repeson = r elif r.status_code in [502, 504]: self.repeson = 'Gateway error' logging.warning(self.repeson) else: self.repeson = 'sever is error' logging.warning(self.repeson) except requests.ConnectionError, e: # 处理DNS网络问题异常 print e except requests.HTTPError, h: # 处理无效http响应异常 print h except requests.Timeout, t: # 处理请求超时异常 print t return self.repeson def Post_cookies(self):#需要cookies的post请求方法 if 'x-www-form-urlencoded' in self.herders.get('Content-Type'): Post_data = urllib.urlencode(self.data) elif 'json' in self.herders.get('Content-Type'): Post_data = json.dumps(self.data) else: Post_data = self.data try: r = requests.post(self.url,data=Post_data,headers=self.herders,cookies=self.cookies,verify=False) if r.status_code == 200: self.repeson = r elif r.status_code in [502, 504]: self.repeson = 'Gateway error' logging.warning(self.repeson) else: self.repeson = 'sever is error' logging.warning(self.repeson) except requests.ConnectionError, e: # 处理DNS网络问题异常 print e except requests.HTTPError, h: # 处理无效http响应异常 print h except requests.Timeout, t: # 处理请求超时异常 print t return self.repeson def Get_token(self):#需要验证token的get请求,每次请求前调用Main.Token中的加密算法生成一个token if 'dev' in str(self.url): #定义开发环境生成token所需的appname和appkey appname = str(self.appname.get('dev')) key = self.key.get('dev') iv = self.iv.get('dev') rang = str(self.tamp) + '|' + appname token = b2a_hex(iv) + self.t.encrypt(rang,key,iv) query_params = urllib.urlencode({'appname': appname, 'token': token}) elif 'pre' in str(self.url): #定义测试环境生成token所需的appname和appkey appname = str(self.appname.get('pre')) key = self.key.get('pre') iv = self.iv.get('pre') rang = str(self.tamp) + '|' + appname token = b2a_hex(iv) + self.t.encrypt(rang, key, iv) query_params = urllib.urlencode({'appname': appname, 'token': token}) else: query_params = '' get_url = self.url+'?'+query_params if 'auth' in self.data.keys(): Auth = self.data.get('auth') data_p = urllib.urlencode(self.data) oldAuth = data_p.split('&')[3].split('=')[1] get_data = str(data_p).replace(oldAuth,Auth) else: get_data = urllib.urlencode(self.data) try: r = requests.get(get_url,params=get_data,verify=False) if r.status_code == 200: self.repeson = r elif r.status_code in [502, 504]: self.repeson = 'Gateway error' logging.warning(self.repeson) else: repeson = 'sever is error' logging.warning(self.repeson) except requests.ConnectionError, e: # 处理DNS网络问题异常 print e except requests.HTTPError, h: # 处理无效http响应异常 print h except requests.Timeout, t: # 处理请求超时异常 print t return self.repeson def Post_token(self):#需要验证token的post请求,每次请求前调用Main.Token中的加密算法生成一个token if 'dev' in str(self.url): #定义开发环境生成token所需的appname和appkey appname = str(self.appname.get('dev')) key = self.key.get('dev') iv = self.iv.get('dev') rang = str(self.tamp) + '|' + appname token = b2a_hex(iv) + self.t.encrypt(rang,key,iv) query_params = urllib.urlencode({'appname': appname, 'token': token}) elif 'pre' in str(self.url): #定义测试环境生成token所需的appname和appkey appname = str(self.appname.get('pre')) key = self.key.get('pre') iv = self.iv.get('pre') rang = str(self.tamp) + '|' + appname token = b2a_hex(iv) + self.t.encrypt(rang, key, iv) query_params = urllib.urlencode({'appname': appname, 'token': token}) else: query_params = '' #query_params = {'appname': self.appname, 'token': token} Post_url = self.url+'?'+query_params if 'x-www-form-urlencoded' in self.herders.get('Content-Type'): #keys = self.data.keys() #values = self.data.values() #for i in range(len(keys)): #if not self.data.get(keys[i]): #self.data.pop(keys[i]) #else: #continue Post_data = urllib.urlencode(self.data) #Post_data = urllib.urlencode(dict(self.data.items()+query_params.items())) elif 'json' in self.herders.get('Content-Type'): Post_data = json.dumps(self.data) else: Post_data = self.data try: r = requests.post(Post_url,data=Post_data,headers=self.herders,verify=False) if r.status_code == 200: self.repeson = r elif r.status_code in [502, 504]: self.repeson = 'Gateway error' logging.warning(self.repeson) else: self.repeson = 'sever is error' logging.warning(self.repeson) except requests.ConnectionError, e:#处理DNS网络问题异常 print e except requests.HTTPError,h:#处理无效http响应异常 print h except requests.Timeout,t:#处理请求超时异常 print t return self.repeson
这是个非常庞大的家伙(说实话现在这个已经是修改无数次之后的结果,仍有不足之处),包含了六种请求实现方法,以及日志和异常的记录,这个就是整个接口测试的核心了,把这个东西写好接下来的业务测试脚本就很简单了,刚才说到的
token验证就对应这里的Post_token()和Get_token()方法,我在这里因为工作需要分别写了不同环境下token的生成步骤,可见注释,具体的步骤就是你写的业务脚本通过调用这个
HttpUntitls进行请求并获得返回体(如果是自动化还需要数据驱动公共资源,可参考我的第一篇博客),这样就可以很好的处理动态验证参数的问题了
随着互联网的高速发展,技术也越来越新奇,我相信后来肯定会出现更加变态的挑战,所以我们要不断的研究提高,这样才能与时俱进!!