python:接口自动化测试框架读取配置优化

软件测试君 2021-11-25 19:25:11
自动化 Python 测试 接口 自动

在这里插入图片描述
前一阶段整理了自己理解的接口自动化项目框架的优化版本,这次把遗漏的环境切换和配置文件读取部分给完善了,如有小过半需要代码的,两篇文章直接复制代码即可。话不多说,进入代码的世界…

配置文件的读取

项目目录中有一个settings.py文件,这里是专门放一些配置数据的,读取到后再进行处理获取需要的值

先看setting.py文件的内容:

from pathlib import Path, PurePath
# 获取项目根目录
BASE_DIR = PurePath(Path(__file__).parent)
# 用于判断是否往企业微信发送测试报告:True是发送、False是不发送
IS_SEND = True
# 设置运行的环境变量
ENVIRONMENT = "PRO" # 环境变量值分别为 测试:TEST;预发布:PRE;生产:PRO
# 接口请求域名
HOST = "http://apis.juhe.cn"
# 设置头信息指定域名和Content-Type类型
HEADERS = {
'Content-Type': 'application/json'}
# 环境IP配置
BASE_HOST = {

"test": None,
"pre": None,
"pro": None,
}
# 数据库配置
DATABASES = {

"pro": {
"host": "8.136.250.157", "port": 1234, "user": "root", "passwd": "test.2016", "db": "testing"},
}
# yaml文件路径
YAML_FILE_PATH = {

"api_idiom": BASE_DIR.joinpath("testDatas", "idiom_modules.yml"),
"api_match": BASE_DIR.joinpath("testDatas", "match_modules.yml"),
}
# 日志存放目录
LOGGING_PATH = BASE_DIR.joinpath("logs", f"logfile.text")
# 日志记录配置
LOGGING_CONFIG = {

"version": 1,
"root": {

"level": "DEBUG",
"handlers": ["file", "console"]
},
"handlers": {

"console": {

"class": "logging.StreamHandler",
"level": "ERROR",
"formatter": "console_formatters"
},
"file": {

"class": "logging.handlers.RotatingFileHandler",
"formatter": "file_formatters",
"filename": LOGGING_PATH,
"level": "DEBUG",
"maxBytes": 100,
"backupCount": 5,
"encoding": "utf-8"
}
},
"formatters": {

"console_formatters": {

"format": "%(asctime)s [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s",
"datefmt": "%Y%m%d %H:%M:%S"
},
"file_formatters": {

'format': "%(asctime)s [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s- %(pathname)s",
"datefmt": "%Y%m%d %H:%M:%S"
}
}
}

名词解释:

  • BASE_DIR是项目的根路径

  • IS_SEND是判断邮件的发送

  • ENVIRONMENT是切换环境

  • HOST是请求的域名

  • HEADERS是默认请求头

  • BASE_HOST是环境IP

  • DATABASES是数据库配置

  • YAML_FILE_PATH是读取yaml文件的路径

  • LOGGING_PATH是日志文件的路径

  • LOGGING_CONFIG日志的配置

是如何读取settings.py中的配置的呢,这也是借鉴了django框架的懒加载,当然我这是简化版的懒加载(可能还没达到懒加载,哈哈…)。这段代码是放置在apiTest/common/init.py中的:

# apiTest/common/__init__.py
import importlib
__all__ = ["setting"]
class LazySetting:
def __init__(self, settings_module):
_mod = importlib.import_module(settings_module)
self._explicit_settings = set()
for setting in dir(_mod):
if setting.isupper():
setting_value = getattr(_mod, setting)
if isinstance(setting, (list, tuple)):
raise TypeError("类型不符合要求!")
setattr(self, setting, setting_value)
self._explicit_settings.add(setting)
def _is_overridden(self, setting):
return setting in self._explicit_settings
def get_setting(self, name):
for i in self._explicit_settings:
if self._is_overridden(i) and (name == i):
return getattr(self, i)
setting = LazySetting("settings")

这段代码,大家可自行理解,我怕解释不清楚,让大家搞混掉了,其实实际操作一下就能看懂个大概了。

settings.py文件中的数据就被拿到了,然后再去进行处理,处理成自己需要的那种:

# apiTest/common/readEnvironment.py
import os
import common
_DATA = common.setting.get_setting
__all__ = ["MappingRelation"]
class Config:
def __getitem__(self, key):
"""
魔术方法,把类变成字典取值
:param key:
:return:
"""
return self.__getattribute__(key)
class Test_DATA(Config):
"""
测试环境配置信息
"""
HOST = _DATA("HOST")
HEADERS = _DATA("HEADERS")
YAML_PATH = _DATA("YAML_FILE_PATH")
LMS_LOGGING = _DATA("LOGGING_CONFIG")
BASE_URL = _DATA("BASE_HOST")["test"]
_DATABASE = _DATA("_DATABASES")["pro"]
class Pre_DATA(Config):
"""
预发布环境配置信息
"""
HOST = _DATA("HOST")
HEADERS = _DATA("HEADERS")
YAML_PATH = _DATA("YAML_FILE_PATH")
LMS_LOGGING = _DATA("LOGGING_CONFIG")
BASE_URL = _DATA("BASE_HOST")["pre"]
_DATABASE = _DATA("_DATABASES")["pro"]
class Pro_DATA(Config):
"""
线上环境配置信息
"""
HOST = _DATA("HOST")
HEADERS = _DATA("HEADERS")
YAML_PATH = _DATA("YAML_FILE_PATH")
LMS_LOGGING = _DATA("LOGGING_CONFIG")
BASE_URL = _DATA("BASE_HOST")["pro"]
_DATABASE = _DATA("_DATABASES")["pro"]
class MappingRelation:
# 设置映射关系
_mapping = {

'PRO': Pro_DATA,
'PRE': Pre_DATA,
'TEST': Test_DATA
}
@property
def map(self):
# 返回映射关系
env = os.environ.get('ENV', _DATA("ENVIRONMENT")).upper() # 配置文件中读取环境变量
return self._mapping[env]() # 获取指定的环境

名词解释:

  • _DATA是common.setting.get_setting方法赋值的变量

  • Config类是写了两个魔术方法,getitem是可变成下标取值的样式,而getattribute是属性拦截器

  • Test_DATA子类继承了Congif类,配置的是测试环境

  • Pre_DATA子类继承了Congif类,配置的是预发环境

  • Pro_DATA子类继承了Congif类,配置的是生产环境

  • MappingRelation类是映射上述三个子类,读取配置文件中的环境变量,去返回对应的环境数据

这里处理完成后,再去编写一个.py文件,专门调用MappingRelation类来获取对应的数据:

# apiTest/common/mapMnvironment.py
from urllib import parse
from common.readEnvironment import MappingRelation
__all__ = ["MapEnvironment"]
class MapEnvironment:
_config = MappingRelation().map
@classmethod
def _command(cls, info):
"""
远程执行命令
:param info:
:return:
"""
return info
@classmethod
def base_url(cls, url):
"""
获取url信息并执行替换操作
:param url:
:return:
"""
if cls._command(cls._config.BASE_URL):
u = parse.urlparse(url)
scheme, netloc, path, params, query = u.scheme, u.netloc, u.path, u.params, u.query
return f'{
scheme}://{
netloc.replace(netloc, cls._command(cls._config.BASE_URL))}{
path}?{
params}{
query}'
else:
return url
@property
def base_db(cls):
"""
获取数据库连接环境账号
:return:
"""
return cls._command(cls._config.DATABASE)
@property
def host(cls):
"""
获取域名
:return:
"""
return cls._command(cls._config.HOST)
@property
def headers(cls):
"""
获取头部信息
:return:
"""
return cls._command(cls._config.HEADERS)
@property
def yaml_path(cls):
"""
读取yaml文件路径
:return:
"""
return cls._command(cls._config.YAML_PATH)
@property
def lms_logging(cls):
"""
读取日志配置
:return:
"""
return cls._command(cls._config.LMS_LOGGING)
if __name__ == '__main__':
print(MapEnvironment().host)

这里处理完成后,各处需要参数的类或函数,就直接引入这个类,拿对应的数据即可。

需要注意的是,如果你settings.py配置文件中需要增加新参数,就得在这两个.py文件中进行增加获取数据的方法,不然就是只是增加,没有起到任何作用。

日志配置

日志的配置也是根据上述两个类,读取到settings.py配置文件的日志配置(这里的日志配置,官网有详细介绍,也是减少代码的一种好处,在公众号中也写了该文章,有兴趣的可以翻阅下):

# apiTest/common/logLogging.py
import logging.config
from common.mapMnvironment import MapEnvironment
class LogLogging:
_logging_conf = MapEnvironment().lms_logging
def __init__(self):
logging.config.dictConfig(self._logging_conf)
self.logger = logging.getLogger()
self.logger.setLevel(logging.ERROR)
@property
def get_logger(self):
return self.logger
do_logger = LogLogging().get_logger

封装的一个日志获取类,代码和配置作了分离,是不是看起来都简洁了呢,哈哈…

发送企业微信报告

import jenkins
import requests
from functools import partialmethod
from common.mapMnvironment import MapEnvironment
class WeChat:
_map = MapEnvironment()
@classmethod
def _message_info(cls, project, end, total, passing, succeed, failing, error, address: str):
"""
需要发送到企业微信的文案信息
:param project: 项目名称
:param end: 指定端[web、app、h5]
:param total: 总计
:param passing: 通过率
:param succeed: 通过数
:param failing: 失败数
:param error: 错误数
:param address: 链接地址->放一个链接信息
:return: 返回data信息
"""
data = {

"msgtype": "markdown", # 消息类型,此时固定为markdown
"markdown": {

"content": "#### **提醒!自动化测试反馈**\n###### **请相关同事注意,及时跟进!**\n"
"> 项目名称:<font color=\"comment\">{}</font> \n"
"> 测试用例总数:<font color=\"comment\">{}</font>,测试用例通过率:<font color=\"comment\">{}</font>\n"
"> **--------------------运行详情--------------------**\n"
"**环境:**<font color=\"comment\"> {} </font>,**端:**<font color=\"comment\">{}</font>\n"
"> **成功数:**<font color=\"comment\">{}</font>,**失败数:**<font color=\"comment\">{}</font>,"
" **错误数:**<font color=\"comment\">{}</font>\n"
"> **报告链接:** [jenkins测试报告,鼠标点击此处查看]({})".format(project, total, passing,
cls._map.environment, end, succeed, failing,
error, address)
# 加粗:**需要加粗的字**
# 引用:> 需要引用的文字
# 字体颜色(只支持3种内置颜色)
# 标题 (支持1至6级标题,注意#与文字中间要有空格)
# 绿色:info、灰色:comment、橙红:warning
}
}
return data
@classmethod
def send_message(cls, project, end, total, passing, succeed, failing, error, address):
"""
请求发送接口
:param project: 项目名称
:param end: 指定端,管理端、APP端等
:param total: 总计
:param passing: 通过率
:param succeed: 通过数
:param failing: 失败数
:param error: 错误数
:param address: 链接地址->放一个链接信息
:return:
"""
# 获取报告文案信息
data = cls._message_info(project, end, total, passing, succeed, failing, error, address)
# 请求接口,开始发送请求
with requests.Session() as session:
response = session.post(url=cls._map.wechat, json=data, verify=False)
return response.json()

这个发送企业微信报告,因为牵扯到项目的一些东西,只提供参考,上述两个方法稍微修改下即可使用。

以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你,如有疑问、歧义,直接私信留言会及时修正发布;非常期待你的赞赏、点赞、在看+分享哟,谢谢!

下面是一份配套资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!
在这里插入图片描述
这些都可以以在公众号:伤心的辣条 ! 免费领取,还有一份216页软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!,其中资料包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

学习不要孤军奋战,最好是能抱团取暖,相互成就一起成长,群众效应的效果是非常强大的,大家一起学习,一起打卡,会更有学习动力,也更能坚持下去。你可以加入我们的测试技术交流扣扣群:914172719(里面有各种软件测试资源和技术讨论)

喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!


好文推荐

转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧!

面试经:一线城市搬砖!又面软件测试岗,5000就知足了…

面试官:工作三年,还来面初级测试?恐怕你的软件测试工程师的头衔要加双引号…

什么样的人适合从事软件测试工作?

那个准点下班的人,比我先升职了…

测试岗反复跳槽,跳着跳着就跳没了…

版权声明
本文为[软件测试君]所创,转载请带上原文链接,感谢
https://blog.csdn.net/ai_green/article/details/121541380

  1. 关于#python_while循环的写法#的问题,如何解决?
  2. Python异常处理中异常的种类有哪些?你知道几个?
  3. Python异常處理中异常的種類有哪些?你知道幾個?
  4. Quels sont les types d'exceptions dans la gestion des exceptions python? Combien en savez - vous?
  5. À propos de # Python Comment résoudre le problème de l'écriture de la Boucle while?
  6. Python如何操作system.data.sqlite数据库
  7. python数字游戏,让你欲罢不能
  8. Python中的可迭代对象、迭代器、For循环工作机制、生成器
  9. 一个从没接触过编程的人,如何自学进入Python行业?
  10. 一份超级实用的 Python ”技巧“清单
  11. Python 程序员给上路初学者的3点忠告
  12. 3 conseils pour les débutants sur la route par les programmeurs Python
  13. Une liste de conseils Python super pratiques
  14. Comment quelqu'un qui n'a jamais été en contact avec la programmation peut - il apprendre à entrer dans l'industrie python?
  15. Objets itérables, itérateurs, pour le mécanisme de travail circulaire, générateurs en python
  16. Les Jeux de chiffres Python vous font vous arrêter
  17. Comment Python fonctionne avec la base de données system.data.sqlite
  18. Python之html与markdown互相转换
  19. Python之html與markdown互相轉換
  20. Conversion HTML et markdown de Python
  21. Python生成的随机数,要怎么设定成随机数a小于随机数b啊
  22. Python生成的隨機數,要怎麼設定成隨機數a小於隨機數b啊
  23. Le nombre aléatoire généré par Python, comment définir le nombre aléatoire a est inférieur au nombre aléatoire B
  24. Python爬虫能做什么
  25. Python老技师给上路初学者的3点忠告
  26. 3 conseils aux débutants sur la route de l'ancien technicien Python
  27. Que peuvent faire les reptiles Python
  28. The code NPM cloned from git will report a python error after it is installed!
  29. 数据分析从零开始实战,Pandas读写CSV数据
  30. Python基础(十二):类与对象
  31. Python Foundation (12): classes et objets
  32. Python基礎(十二):類與對象
  33. L'analyse des données commence à zéro et pandas lit et écrit les données CSV
  34. Python Qt GUI设计:QSlider滑动条类(基础篇—16)
  35. #yyds干货盘点#数据分析从零开始实战,Pandas读写CSV数据
  36. This paper designs an examination system for automatically setting questions and judging papers by using Python standard library language to investigate the ability of users to calculate four integers within 100.
  37. Yyds Dry Inventory # Data Analysis started from zero, pandas read and write CSV Data
  38. python数据结构:数组、列表、栈、队列及实现
  39. Python编程真的是未来人工智能的主流吗?
  40. Python编写通讯录,支持模糊查询,利用数据库存储
  41. Python編寫通訊錄,支持模糊查詢,利用數據庫存儲
  42. Python編程真的是未來人工智能的主流嗎?
  43. Python écrit le carnet d'adresses, prend en charge les requêtes floues et utilise le stockage de base de données
  44. La programmation Python est - elle vraiment le courant dominant de l'IA future?
  45. Structure des données Python: tableaux, listes, piles, files d'attente et implémentations
  46. Python networkx Practical Social Network Visualization
  47. [译] 通过 for 循环,比较 Python 与 Ruby 编程思想的差别
  48. Comparez les différences de programmation entre Python et Ruby à travers la boucle for
  49. Python basic and introductory tutorials
  50. What you don't know
  51. 人生苦短,能让你更早下班的Python垃圾回收机制
  52. Boring playing Tetris, using Python to do their own without advertising
  53. Seemingly boring Python games, but I fished for work all afternoon!!
  54. A collection of grammar knowledge points for getting started with Python
  55. Benefits of learning Python
  56. Play childhood memories with Python, greedy snake
  57. Python is suitable for beginners
  58. Simple Python game making
  59. Welfare is coming!! Python basic syntax dry goods
  60. Simply understand the learning direction of Python and make yourself a better choice