...

30行左右代码实现一个类似httprunner的接口框架

框架的最终归宿往往是领域语言+模板解析。
首先先约定一种所要执行操作的表述格式。然后通过模板解析将描述语言转化为代码进行执行。例如,我们可以使用以下yaml文件描述多个步骤并且需要关联的接口:
apis.yaml:

- name: 获取百度token接口 # 接口名称
  request:  # 请求报文
    url: https://aip.baidubce.com/oauth/2.0/token
    method: get
    params:
      grant_type: client_credentials
      client_id: kPoFYw85FXsnojsy5bB9hu6x
      client_secret: l7SuGBkDQHkjiTPU3m6NaNddD6SCvDMC
  extract:  # 提取变量, 字典格式
    token:  RESPONSE.json()['access_token']  # RESPONSE系统变量,代表响应对象

- name: 百度ORC接口  # 第二个接口
  request:
    url: https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=${token}  # 使用变量
    method: post
    data:  # 请求体(表单格式)
      url: //upload-images.jianshu.io/upload_images/7575721-40c847532432e852.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240
  verify:  # 断言, 列表格式
    - RESPONSE.json()['words_result_num'] == 6

其中,name为该接口或步骤的名称描述,request段是接口的信息,对应requests.request()的每一个参数,urlmethod是必要参数,params,headers,cookies,data, json,files,timeout 等,并使用对应格式。extract用于提取值, token: RESPONSE.json()['access_token'] ,表示提取该接口响应字典中的access_token字段的值保存为名为token的变量。
在第二个接口的url中通过使用${token},引用该变量。
verify段类似与extract, 计算表达式的值,通过结果的True/False判断该条断言是否通过。

上面我们定义了一套接口关联的描述及规则,下面我们要对我们的规则进行解析,并加载运行,主要分为以下几步:

  1. 读取yaml文件并使用yaml.safe_load(f)转为列表/字典
  2. 遍历列表,每个列表项是一个接口
  3. 读取当前列表项(接口)的request段信息,处理$
    1. 将request段(字典格式)重新转会yaml字符串
    2. 如果包含$使用string.Template('字符串').safe_subtitute(locals()),从locals()当前所有局部变量中找到$表示的同名变量,如token,并替换。
    3. 重新将替换后变量的字符串转化为字典
  4. 字典拆包,发送request请求
  5. 如果请求中有extract字段,使用eval()计算表达式的值并保存到局部变量locals()中。
  6. 如果请求中有verify字段,使用eval()计算表达式的值,并判断真假。

实现代码:

需要安装pyyaml: pip install pyyaml

apis_parser.py

import yaml
import requests
from string import Template

with open('apis.yaml', encoding='utf-8') as f:
    apis = yaml.safe_load(f)

for api in apis:
    print("处理请求:", api.get('name'))
    request = api.get('request', {})  # 请求报文,默认值为{}
    # 处理参数化请求中的${变量}
    request_str = yaml.dump(request)  # 先转为字符串
    if '$' in request_str:
        request_str = Template(request_str).safe_substitute(locals())  # 替换${变量}为局部变量中的同名变量
        request = yaml.safe_load(request_str)  # 重新转为字典
    # 发送请求
    res = requests.request(**request)  # 字典解包,发送接口
    # 提取变量
    extract = api.get('extract')
    if extract is not None:  # 如果存在extract
        for key, value in extract.items():
            # 计算value表达式,可使用的全局变量为空,可使用的局部变量为RESPONSE(响应对象)
            # 保存变量结果到局部变量中
            print("提取变量:", key, value)
            locals()[key] = eval(value, {}, {'RESPONSE': res})  
    # 处理断言
    verify = api.get('verify')
    if verify is not None:
        for line in verify:
            result = eval(line, {}, {'RESPONSE': res}) # 计算断言表达式,True代表成功,False代表失败
            print("断言:", line, "结果:", "PASS" if result else "FAIL") 

执行结果:

处理请求: 获取百度token接口
提取变量: token RESPONSE.json()['access_token']
处理请求: 百度ORC接口
断言: RESPONSE.json()['words_result_num'] == 6 结果: PASS
posted @ 2019-09-03 19:19  韩志超  阅读(556)  评论(0编辑  收藏  举报