当前位置: 首页 > news >正文

API 接口自动化测试详细图文教程学习系列17--项目实战演练4-封装方法

测试学习记录,仅供参考!

项目实战演练-封装方法

配置文件的读取封装处理

建议可参考一:使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 30--开源电商商城系统项目实战--配置测试环境地址

建议参考二:使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 35--二次封装MySQL数据库连接操作

1、 在 configs 目录文件下新建名称为config.ini的文件,用于配置数据库相关信息;自定义文件名称、格式,具体需要用什么类型的配置文件看个人习惯,可以使用其他格式的文件,建议使用常见的或者自己个比较熟悉的,亦可直接按步骤操作;

2、在config.ini配置文件输入以下内容;此信息来源于测试接口服务项目,若有其他方面的信息,可自行配置(若换了数据库可直接更改配置文件内容即可);

[Host] host = http://127.0.0.1:8787 [MySQL] host = localhost port = 3306 username = root password = 123456 databases = db

3、如若一个项目中只有一个config.ini配置文件时,可以把“需要解析的配置文件路径 file_path ”写到 config 软件包文件目录下setting.py文件中去;

修改 configs 目录文件下的setting.py文件,文件路径中增加ini选项,使用 os.path.join(DIR_PATH, 'configs', 'config.ini') 把路径拼接一下,若是只有一个config.ini配置文件,可以把路径写为固定的;

  • 通过根目录(DIR_PATH)+ 目录( configs)+ 文件( config.ini)拼接成一个完整的目录文件路径;
  • 调试打印测试 print(FILE_PATH['ini']) ,通过读取 FILE_PATH['ini'] 拿到项目中的.ini配置文件路径;

'extract' : os.path.join(DIR_PATH, 'extract.yaml')--根目录、目标文件--这个结构是两层,它extract.yaml是直接属于根目录的,所以不需要去写父级目录,因为它的父级目录就是根目录;

'ini' : os.path.join(DIR_PATH, 'configs', 'config.ini')--根目录、目标文件的父级目录、目标文件--这个结构是三层,它config.ini的父级目录在根目录的下一级,所以需要再加一层;

# 导包 import os import sys # 使用os.path.dirname获取当前文件所在目录的父目录--也就是说获取当前文件的上一级目录 # 这里需要拿到项目的根目录,所以再叠加一次 DIR_PATH = os.path.dirname(os.path.dirname(__file__)) # 找到项目根目录之后需要把它加到 搜索模块里面去 # 把项目根目录添加到系统的路径列表中--目的是:确保可以通过导入模块的方式找到项目根目录 sys.path.append(DIR_PATH) # 新建一个字典 FILE_PATH 变量来定义文件路径 FILE_PATH = { # 这样做的好处是:其他地方需要用到时可直接引用 setting.py模块 'extract' : os.path.join(DIR_PATH, 'extract.yaml'), # 在这个字典里面再定义一个叫ini的元素 # 通过它的根目录、父级目录、再加一个它的文件--通过这三组就可以把它拼接成一个完整的绝对路径 'ini': os.path.join(DIR_PATH, 'configs', 'config.ini') } # 打印出文件路径--通过字典key值可以拿到文件路径 # print(FILE_PATH['extract']) # print(FILE_PATH['ini'])

修改完成后可自行测试打印查看结果是否准确;

4、 配置文件写完之后,还需要专门写一个方法来解析读取配 ini 格式文件;在项目根目录 unit_tools 软件包 handle_data 软件包下新建一个名称为configParse.py的 Python 文件,输入以下内容;

导包 import configparser :configparser 库不需要安装,内置库,直接导入即可;

# 导包 import configparser # 引进 FILE_PATH 字典 from configs.setting import FILE_PATH # 这里需要解析配置文件--直接写成类的方式--定义一个类 configParser class ConfigParser: """ 解析.ini后缀的配置文件 """ # 初始化构造函数 __init__(self)--需要解析哪个文件? # 给它传一个路径--一般配置文件一个就够了。这里路径给它一个默认值 def __init__(self, file_path=FILE_PATH['ini']): # 给它定义一个初始化属性self.file_path--它的值就等于传进来的默认值 file_path self.file_path = file_path # 调用configparser模块去解析,点它的一个类 .ConfigParser(),把结果返回给一个对象 self.config self.config = configparser.ConfigParser() # 在初始化时需要先读取这个文件 self.config.read(self.file_path)-- .read_config()调用这个方法 self.read_config() # 再定义一个方法--获取到读取哪个文件 def read_config(self): # 通过创建的这个对象 self.config--去调用类 .read()--读取路径 self.file_path self.config.read(self.file_path) # 定义方法get_value--两个参数 section值相当于配置文件中的[Host]级,option相当于[Host]下面的host级 def get_value(self, section, option): """ 获取配置文件的值 :param section: 头参数 :param option: 下级参数key值 :return: """ # 添加异常处理--先写异常处理结构格式 try: # 直接return--直接去调用self.config--这个对象 .get方法 # 把 section, option 这两个值传进来 return self.config.get(section, option) except Exception as e: print(f'解析配置文件出现异常,原因:{e}') # 测试调试,查看一下是否生效 if __name__ == '__main__': # 实例化 ConfigParser() conf = ConfigParser() # 通过类的对象直接去调用 封装的 get_value 方法--传入 头参、下级参数key值 res = conf.get_value('Host', 'host') # 打印查看一下 print(res)

5、若此时删除config.ini配置文件中的参数“host = http://127.0.0.1:8787”,再次运行configParse.py文件,则会抛出异常提醒;

6、修改configParse.py文件内容,再写一级,专门写一个方法,可以不用传 头部参数,只传下级参数 key 值;

# 导包 import configparser # 引进 FILE_PATH 字典 from configs.setting import FILE_PATH # 这里需要解析配置文件--直接写成类的方式--定义一个类 configParser class ConfigParser: """ 解析.ini后缀的配置文件 """ # 初始化构造函数 __init__(self)--需要解析哪个文件?给它传一个路径--一般配置文件一个就够了。这里路径给它一个默认值 def __init__(self, file_path=FILE_PATH['ini']): # 给它定义一个初始化属性self.file_path--它的值就等于传进来的默认值 file_path self.file_path = file_path # 调用configparser模块去解析--点它的一个类 .ConfigParser()--把结果返回给一个对象 self.config self.config = configparser.ConfigParser() # 在初始化时需要先读取这个文件 self.config.read(self.file_path)-- .read_config()调用这个方法 self.read_config() # 再定义一个方法--获取到读取哪个文件 def read_config(self): # 通过创建的这个对象 self.config--去调用类 .read()--读取路径 self.file_path self.config.read(self.file_path) # 定义方法get_value--两个参数 section值相当于配置文件中的[Host]级,option相当于[Host]下面的host级 def get_value(self, section, option): """ 获取配置文件的值 :param section: 头参数 :param option: 下级参数key值 :return: """ # 添加异常处理--先写异常处理结构格式 try: # 直接return--直接去调用self.config--这个对象 .get方法--把 section, option 这两个值传进来 return self.config.get(section, option) except Exception as e: print(f'解析配置文件出现异常,原因:{e}') # 再写一个方法get_host--传一个option参数--头参下面的下级参数 def get_host(self, option): """ 专门获取接口的服务器ip地址信息 :param option: :return: """ # return--直接通过self去调用.get_value方法--这里需要写两个参数,section直接写死为Host,然后再传一个这里面的option return self.get_value('Host', option) # 测试调试查看一下是否生效 if __name__ == '__main__': # 实例化 ConfigParser() conf = ConfigParser() # 调用 get_host 这个方法只需要传一个参数了--传入下级参数key值 res = conf.get_host('host') # 打印查看一下 print(res)

封装一个用于获取MySQL配置信息的方法

7、再次优化,封装一个方法用于获取 MySQL 数据库配置信息,修改configParse.py文件内容;

# 导包 import configparser # 引进 FILE_PATH 字典 from configs.setting import FILE_PATH # 这里需要解析配置文件--直接写成类的方式--定义一个类 configParser class ConfigParser: """ 解析.ini后缀的配置文件 """ # 初始化构造函数 __init__(self)--需要解析哪个文件?给它传一个路径--一般配置文件一个就够了。这里路径给它一个默认值 def __init__(self, file_path=FILE_PATH['ini']): # 给它定义一个初始化属性self.file_path--它的值就等于传进来的默认值 file_path self.file_path = file_path # 调用configparser模块去解析--点它的一个类 .ConfigParser()--把结果返回给一个对象 self.config self.config = configparser.ConfigParser() # 在初始化时需要先读取这个文件 self.config.read(self.file_path)-- .read_config()调用这个方法 self.read_config() # 再定义一个方法--获取到读取哪个文件 def read_config(self): # 通过创建的这个对象 self.config--去调用类 .read()--读取路径 self.file_path self.config.read(self.file_path) # 定义方法get_value--两个参数 section值相当于配置文件中的[Host]级,option相当于[Host]下面的host级 def get_value(self, section, option): """ 获取配置文件的值 :param section: 头参数 :param option: 下级参数key值 :return: """ # 添加异常处理--先写异常处理结构格式 try: # 直接return--直接去调用self.config--这个对象 .get方法--把 section, option 这两个值传进来 return self.config.get(section, option) except Exception as e: print(f'解析配置文件出现异常,原因:{e}') # 再写一个方法get_host--传一个option参数--头参下面的下级参数 def get_host(self, option): """ 专门获取接口的服务器ip地址信息 :param option: :return: """ # return--直接通过self去调用.get_value方法--这里需要写两个参数,section直接写死为Host,然后再传一个这里面的option return self.get_value('Host', option) # 再定义一个get_mysql_conf方法--获取mysql配置信息 def get_mysql_conf(self, option): """ 获取MySQL数据库的配置参数值 :return: """ # 直接return--self.get_value去调用 return self.get_value('MySQL', option) # 测试调试查看一下是否生效 if __name__ == '__main__': # 实例化 ConfigParser() conf = ConfigParser() # 调用 get_mysql_conf 方法获取MySQL配置信息 res = conf.get_mysql_conf('host') # 打印查看一下 print(res)

8、把需要拼接的 IP 地址 字符串改为取值配置文件中的参数,修改项目根目录 unit_tools 软件包下sendrequests.py文件内容;

# 引入模块--导包 import requests from requests import utils import re from unit_tools.handle_data.yaml_handler import read_yaml, write_yaml # 使用类的方式去封装--定义一个类 SendRequests class SendRequests: # 使用 __init__ 创建构造函数 def __init__(self): # 占位符 pass # 类方法 加上 @classmethod 就是一个类方法 # 类方法不能去访问实例 初始化构造函数里面的属性,只能访问类的属性--需要把 self 改为 cls @classmethod # 定义一个方法 _text_encode --传一个接口返回的文本格式 res_text def _text_encode(cls, res_text): """ 处理接口返回值出现unicode编码时 例如:\\u767b :param res_text: :return: """ # 调用 re.search --去搜索这个文本(匹配正则表达式),第二个参数是接口的返回值res_text match = re.search(r"\\u[0-9a-fA-F]{4}", res_text) # 判断匹配的结果是否存在 if match: # 当匹配的结果存在时,把接口的返回值进行处理转换--先把它转码然后再解码 result = res_text.encode().decode('unicode_escape') else: # 若不存在,则直接把接口的返回值传过来 result = res_text # 最后return return result # 直接封装一个 send_request 方法--给它传一个可变数量的参数 **kwargs def send_request(self, **kwargs): # 创建一个会话--使用 requests.Session() 调用这个类并把它的结果返回出去 # 把创建的这个会话对象返回赋值给 session session = requests.Session() # 定义一个变量response并赋值为空 response = None # 这里最好再添加一个异常处理 try: # 直接session.调用request()把可变数量参数**kwargs给它传进来,返回赋值给 response response = session.request(**kwargs) # 通过调用requests这个模块--通过接口的返回值 response.cookies # 通过接口的返回对象获取cookies--定义变量set_cookie去接收 set_cookie = requests.utils.dict_from_cookiejar(response.cookies) # 判断是否有这个cookie if set_cookie: # 暂时注释--不做打印,后续再写入到文件中--写入到文件中之后,以后再次使用就比较方便 # print(f'获取到Cookies: {set_cookie}') pass # 调用self._text_encode方法--结果返回值response.text res = self._text_encode(response.text) print(res) # 连接异常 requests.exceptions.ConnectionError except requests.exceptions.ConnectionError: print("接口请求异常,可能是request的链接数过多或者速度过快导致程序报错!") # 请求异常 except requests.exceptions.RequestException as e: print(f'请求异常,请检查系统或数据是否正常!错误信息为:{e}') # 把发起接口请求的结果返回出去 return response # 再定义一个方法 execute_api_request--再来封装一个方法 # 这里传的参数比较多,一部分是data.yaml配置文件中的字段,最终根据request里面所需要的参数 # api_name, url, method, header--yaml配置文件;case_name--自定义; # cookie默认为空,file文件上传,这里也传空;最后再传可变数量参数 **kwargs def execute_api_request(self, api_name, url, method, header, case_name, cookie=None, file=None, **kwargs): """ 发起接口请求 :param api_name: 接口名称--后续打印日志会用到 :param url: 接口地址 :param method: 请求方法 :param header: 请求头 :param case_name: 测试用例名称--后续打印日志会用到 :param cookie: cookie :param file: 文件上传 :param kwargs: 未知数量的关键字参数 :return: """ # 直接使用self.调用上面封装的方法send_request() # 传参格式,这里的关键字务必跟request里面的关键字参数一致 # 关键字要接收的参数值是 上面定义的这个函数execute_api_request中的 参数, # 需要哪个传给哪个,一一对应即可--再把未知数量传参**kwargs给放进来 # timeout=10--接口超时设置10秒;verify=False--忽略HTTPS证书校验,默认verify=None # 最后把结果给返回出去并赋值给 response response = self.send_request(method=method, url=url, headers=header, cookies=cookie, files=file, timeout=10, verify=False, **kwargs) # 使用 return 直接返回结果 return response # 这里使用登录接口进行测试验证 if __name__ == '__main__': # 在这里引入或者最上面导入都行--一般都是在文件最上方导入 from unit_tools.handle_data.configParse import ConfigParser # 实例化对象ConfigParser()--调用get_host()方法--传入头参下面的下级参数值 host = ConfigParser().get_host('host') # 调用引用的模块里面的read_yaml()方法--相对路径 .././datas/login.yaml # 再拿到列表中的第一组元素--ps:不确定时可以打印出来详细查看 data = read_yaml('.././datas/login.yaml')[0] # 地址--服务器ip地址 + 接口地址 url = host + data['baseInfo']['url'] # 请求方法 同样依次取 key 值 method = data['baseInfo']['method'] # 请求头 同样依次取 key 值--一般都会需要请求头,除非特殊处理 header = data['baseInfo']['header'] # 入参--找到与baseInfo同级的testCase进行取值--testCase里面是一个列表--它里面只有一组数据--取对应值data req_data = data['testCase'][0]['data'] # 查看校验封装的方法能不能正常运行--SendRequests() 实例化这个类--把结果返回出去给自定义的变量对象 send send = SendRequests() # 这里传上面刚刚定义的变量 地址 url、请求方式 method、表头 header、入参 req_data # 这里请求头在接口中已特殊处理过,一般情况下都是需要正常传递的 res = send.execute_api_request(api_name=None, url=url, method=method, header=None, case_name=None, data=req_data) # 这里res的结果返回值response.text--它打印的是一个text属于字符串,字符串不能通过key值去取--所以需要先转换成一个json格式 res_json = res.json() # 转换成功之后即可获取它的token值 token = res_json['token'] # 获取它的userId user_id = res_json['userId'] # 调用write_yaml()方法写入文件 write_yaml({'token': token, 'userId': user_id})

9、最后优化configParse.py文件内容,去掉测试调式if __name__ == '__main__':主函数部分;

# 导包 import configparser # 引进 FILE_PATH 字典 from configs.setting import FILE_PATH # 这里需要解析配置文件--直接写成类的方式--定义一个类 configParser class ConfigParser: """ 解析.ini后缀的配置文件 """ # 初始化构造函数 __init__(self)--需要解析哪个文件?给它传一个路径--一般配置文件一个就够了。这里路径给它一个默认值 def __init__(self, file_path=FILE_PATH['ini']): # 给它定义一个初始化属性self.file_path--它的值就等于传进来的默认值 file_path self.file_path = file_path # 调用configparser模块去解析--点它的一个类 .ConfigParser()--把结果返回给一个对象 self.config self.config = configparser.ConfigParser() # 在初始化时需要先读取这个文件 self.config.read(self.file_path)-- .read_config()调用这个方法 self.read_config() # 再定义一个方法--获取到读取哪个文件 def read_config(self): # 通过创建的这个对象 self.config--去调用类 .read()--读取路径 self.file_path self.config.read(self.file_path) # 定义方法get_value--两个参数 section值相当于配置文件中的[Host]级,option相当于[Host]下面的host级 def get_value(self, section, option): """ 获取配置文件的值 :param section: 头参数 :param option: 下级参数key值 :return: """ # 添加异常处理--先写异常处理结构格式 try: # 直接return--直接去调用self.config--这个对象 .get方法--把 section, option 这两个值传进来 return self.config.get(section, option) except Exception as e: print(f'解析配置文件出现异常,原因:{e}') # 再写一个方法get_host--传一个option参数--头参下面的下级参数 def get_host(self, option): """ 专门获取接口的服务器ip地址信息 :param option: :return: """ # return--直接通过self去调用.get_value方法--这里需要写两个参数,section直接写死为Host,然后再传一个这里面的option return self.get_value('Host', option) # 再定义一个get_mysql_conf方法--获取mysql配置信息 def get_mysql_conf(self, option): """ 获取MySQL数据库的配置参数值 :return: """ # 直接return--self.get_value去调用 return self.get_value('MySQL', option)

未完待续。。。

http://www.jsqmd.com/news/801310/

相关文章:

  • GStreamer在Windows下的Mingw与MSVC版本选择:C#开发者必须搞清楚的DLL依赖问题
  • 如何快速解密QMC音频文件:qmc-decoder完整使用指南
  • 抖音下载器终极指南:3种高效方式批量获取无水印视频
  • VLP-16激光雷达的‘双回波’模式详解:在ROS中如何配置与利用它进行地面分割和障碍物检测
  • 德尔·考德威尔:从微波校准到计量标准,塑造现代精密测量的隐形基石
  • 架构决策记录(ADR)实践指南:使用decision-kit提升团队决策质量
  • QQ音乐加密文件解密终极指南:qmcdump工具完全使用教程
  • 终极指南:Nucleus Co-Op如何让你在一台电脑上玩转分屏多人游戏
  • openclaw-auto-dream-lite:快速构建MVP的自动化脚手架工具
  • 2026年盐城同色定制大揭秘,哪家靠谱看完这篇就知道!
  • 3步搞定无损音乐自由:网易云音乐歌单批量下载终极指南
  • 2026年鑫玖田焊割品牌推荐,靠谱吗 - 工业品牌热点
  • WarcraftHelper:魔兽争霸3终极优化解决方案,告别卡顿畅享经典
  • Cursor编辑器集成OpenAPI Agent:让AI编程助手具备真实API调用能力
  • 基于SpringBoot的企业客户管理系统(附源码)
  • 基于堆叠自编码器与LSTM的金融时间序列预测框架解析
  • 录音转文字在线版有哪些?免费录音转文字在线工具怎么选?2026 年实操对比
  • Capacitor插件开发实战:跨平台移动应用与硬件交互指南
  • 新手必看:用Docker在Kali上5分钟搞定AWD训练平台(附阿里云镜像加速)
  • 从零构建STM32蓝牙遥控车:基于CubeMX与HAL库的硬件驱动与无线通信详解
  • 如何用WeChatExporter轻松备份微信聊天记录:Mac用户的完整解决方案
  • 用Python和statsmodels搞定因果推断:手把手教你实现边缘结构模型(MSM)
  • 【神经网络】前向传播和反向传播
  • Windows窗口置顶终极指南:AlwaysOnTop让你的重要窗口永不遮挡
  • 白起杀降将卒,项羽杀降,黄巢他们有的选择吗?
  • GroundAPI:为AI Agent构建实时数据中台的三种接入方式与实践
  • 从机器学习转做DFT计算?手把手教你用Python ASE库搞定VASP输入文件(含VC++14安装避坑)
  • 差旅平台有哪些?企业差旅痛点与行业发展分析 - 匠言榜单
  • 怎样高效管理微信社交网络:5个微信工具箱实用技巧完整指南
  • Claude-Code-安装配置指南-Windows