动起来才有效果---接口测试进阶,认识复杂的验证逻辑

最近接手一个视频点播项目,刚看到wiki中的描述时有点蒙

 

一个接口有两种请求方式?这个接口明显是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进行请求并获得返回体(如果是自动化还需要数据驱动公共资源,可参考我的第一篇博客),这样就可以很好的处理动态验证参数的问题了
随着互联网的高速发展,技术也越来越新奇,我相信后来肯定会出现更加变态的挑战,所以我们要不断的研究提高,这样才能与时俱进!!



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值