0


Python+Requests+PyWebIO框架详解,编写测试工具提高团队测试效率

一、背景

老铁们如果是QA,想必也遇到过类似痛点吧:

  • 业务逻辑复杂性决定测试场景复杂性,配置测试场景常常花费大量时间,导致测试效率降低
  • 新用户的测试场景,账号可能经常注销,协助debug时需要用userid,每次都得重新抓包。而且测试账号很多,来回切,即使在本地管理userid,维护成本也很大,不如现用现查来的准确可靠
  • 问开发要了一些接口,通过接口配置测试场景,相较于手工配置方便了一些。但如果涉及到多个接口层级调用,配置一个账号还算方便,当配置多个账号时也是特别麻烦,使用Postman就需要编写脚本,用起来我个人感觉还不如Python方便
  • 最需要测试工具的是QA,开发和PM也需要但不是很频繁。如果是本地的Python脚本,其他人要用,每次都需要你去帮忙操作。密集测试时,这种操作的需求太多,别人的效率提高了,你的效率反而降低了。。

怎么样可以“一劳永逸”呢?编写出来工具,方便所有人使用?
使用Python+Requests+PyWebIO开发框架,基于这个框架编写测试工具,部署后,团队内部通过链接即可访问,无需配置任何环境(有浏览器就够),可以实现所有人完全 自助 使用,真正地能提高团队效率。

有了这个框架,只要后端有接口,就能开发成工具。再也不用愁某些Feature 回归成本高了。而且仅通过输入手机号就能查询各种信息,简直不要太方便啦。

这个框架熟悉Python的测试同学很快可以上手,自己设计自己开发,怎么方便怎么来,简直太香了。

二、框架设计

1、api
    1)存放接口模板,我一般以host命名,此host下的所有接口都存放在此python下。
2、common
    1)api_key:get(), post(), jsonpath提取方法的封装。
    2)base_func:对常用字段的解析方法进行封装
    3)my_requests:用户输入接口参数值后,替换掉模板中的数据,并调用api_key中的get, post方法发送请求
3、config
    1)settings:获取文件路径、以及数据库连接信息配置(如有)等
    2)VAR:存放常量,例如手机号的白名单list、测试环境的一些选项等
4、debug
    1)一个python脚本,可发送post请求,用于debug
5、util
    1)get_available_port:部署服务时,查询可用端口号
    2)int_redis(如需要):redis的连接、查询以及删除等操作
    3)my_logger(如需要):logger的封装,此框架中我实际并未使用到。
    4)mysql_client(如需要):mysql的连接、查询以及删除等操作
6、webio
    1)common_ui:像下拉菜单、输入框、单选复选框等,可以进行封装。需要时进行调用。也可以将首页作为一个独立的UI进行封装,每个PyWebIO对其进行调用,实现每个工具都可以进入首页。(PyWebIO有提供多application的start方法,但是我觉得没有这种方式好管理)
    2)homepage:对所有工具进行一个统一展示,可以是各种你喜欢的形式,我一般用表格输出,工具名称,地址,以及描述,owner等信息。此python需要作为PyWebIO app来部署。
    3)tool_x:编写你的测试工具UI以及业务逻辑,py文件可以根据你的业务逻辑进行命名,代码结构中仅是一个示例。此python需要作为PyWebIO app来部署
7、main
    1)project根目录下的main.py,执行它之后,它将会收集所有PyWebIO app,并且部署服务

在这里插入图片描述

三、框架详解

1、api层

shop_xo.py

1.1 设计说明

  • python文件以host进行命名,方便管理
  • 一个api python中,存放此host下的所有接口
  • 将Host作为全局变量,且根据实际的环境进行区分,方便测试工具在不同环境下的使用。我的项目中仅Int和Prod两个环境。
  • headers如果这些接口都一样,可以单独拿出来,不用每个接口都写。demo中的login请求不用填写headers。
  • 每一个func都以接口名直接命名(后端大佬接口命名已经很make sense啦,并且这个仅仅是接口模板,并无业务逻辑)
  • 接口 func的设计:一个请求所需的全部数据作为一个字典进行存储 (key的命名一定要跟get和post方法的参数名一致)。由于为了知道是int还是prod,所以请求数据中有IntUrl和ProdUrl。最终发送请求如何处理,请见my_requests的设计。
  • 对于eval使用不了解的可以看我之前的一篇文章:getattr和eval在Python接口测试中的应用

1.2 代码实现

IntHost ="http://shop-xo.hctestedu.com/index.php?"
ProdHost ="http://shop-xo.hctestedu.com/index.php?"# Headers = {'Content-Type': 'application/json'}

param_data ={
   "application":"app","application_client_type":"weixin"}defLogin(accounts, pwd):
    api_path ="s=api/user/login"
    data ={
   "accounts": accounts,"pwd": pwd,"type":"username"}
    request_info ={
   "IntUrl": IntHost + api_path,"ProdUrl": ProdHost + api_path,"method":"post",# "headers": eval(f'{Headers}'),'data':eval(f'{
     data}'),"params": param_data
    }return request_info
    
defYourGetRequests(userid):
    api_path =f"/YourGetRequests?userid={
     userid}"
    request_info ={
   "IntUrl": IntHost + api_path,"ProdUrl": ProdHost + api_path,"method":"get","headers":eval(f'{
     Headers}')}return request_info   
     
if __name__ =='__main__':print(Login("hello","world"))

2、common层

2.1 api_key

2.1.1 设计说明

  • 发送请求,常用的是post、get请求,对其进行封装
  • 收到响应后,需要对响应结果进行解析,因此对jsonpath的使用进行封装
  • 有时需要用到断言,也可以一并封装

2.1.2 代码实现

import json
import jsonpath
import requests

# 工具类classApiKey:# get请求的封装:因为params可能存在无值的情况,存放默认Nonedefget(self, url, params=None,**kwargs):# return requests.get(url=url, params=params, **kwargs)  # 使用这种方式,需要字典中不包含method参数return requests.request(url=url, params=params,**kwargs)# 使用这种方式,需要字典中带有method参数# post请求的封装:data也可能存在无值得情况,存放默认Nonedefpost(self, url, data=None,**kwargs):# return requests.post(url=url, data=data, **kwargs)return requests.request(url=url, data=data,**kwargs)# 基于jsonpath获取数据的关键字:用于提取所需要的内容defget_text(self, data, key):# jsonpath获取数据的表达式:成功则返回list,失败则返回false# loads是将json格式的内容转换为字典的格式# jsonpath接收的是dict类型的数据
        dict_data = json.loads(data)
        value = jsonpath.jsonpath(dict_data, key)ifisinstance(value,list):iflen(value)==1:
                value = value[0]return value

    # 断言封装defmy_assert(self, acutal, expect):try:assert acutal == expect
        except:return"断言失败"else:return"断言成功"

2.2 my_requests

2.2.1 设计说明

构造方法中的两个成员变量

  • environment:用户在使用工具时,选择的环境
  • api:在api层,host下的接口func

请求模板中参数的替换

  • 将用户输入的数据,作为模板中的实参,在调用接口func时例如,Login(“zz”, “123456”),可以直接替换掉

类方法:get_request_info()

  • 根据用户选择的环境,确定请求url,确定后需要删除字典中的IntUrl和ProdUrl,新增url

类方法:send_requests()

  • 使用get_request_info()拿到所有请求数据之后,需要对请求method做一个判断,然后调用api_key中的post或者get方法,调用时,使用了getattr。
  • 对于getattr使用不了解的,可以看我之前的一篇文章:getattr和eval在Python接口测试中的应用
  • 为什么注释掉了del dict_data[“method”],可以结合上面api_key的get和post代码中的注释。使用getattr时,dict中的key需要跟post、get方法中参数名保持一致,不然就会报错,重复或者post、get方法中不存在的参数名都不行。
  • 使用dumps将响应体转换成Json格式。并且ensure_ascii设置成False,后面put_code打印响应体时,中文就能正常展示。indent=4,可以让输出为标准的Json格式,可读性更强。

2.2.2 代码实现

import json
from json import JSONDecodeError
from api.shop_xo import Login
from common.api_key import ApiKey

classMyRequests:# 初始化方法def__init__(self, environment, api):# 测试环境
        self.environment = environment.upper()# 接口数据
        self.api = api
        self.ak = ApiKey()defsend_requests(self):global resp
        dict_data = self.get_request_info()# dict类型if dict_data["method"]=="post":# del dict_data["method"]
            resp =getattr(self.ak,'post')(**dict_data)elif dict_data["method"]=="get":
            resp =getattr(self.ak,'get')(**dict_data)if resp.status_code >=200and resp.status_code <300:try:
                json_resp = resp.json()# 是字典
                json_resp = json.dumps(json_resp, indent=4, ensure_ascii=False)# dumps()将字典转换成Json, loads()将Json转换成字典except JSONDecodeError as e<
标签: python web 测试工具

本文转载自: https://blog.csdn.net/weixin_44691253/article/details/131656464
版权归原作者 Summer@123 所有, 如有侵权,请联系我们删除。

“Python+Requests+PyWebIO框架详解,编写测试工具提高团队测试效率”的评论:

还没有评论