优化了执行器的全局异常捕获;修改了介绍文件;

This commit is contained in:
毛鹏
2024-04-29 17:01:14 +08:00
parent b3d79d664e
commit 288a91b1c5
22 changed files with 158 additions and 160 deletions

BIN
API执行演示.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@@ -4,8 +4,8 @@
# @Time : 2023-07-15 11:57
# @Author : 毛鹏
from autotest.ui.base_tools.web import WebDevice
from autotest.ui.base_tools.android import AndroidDriver
from autotest.ui.base_tools.web import WebDevice
from enums.tools_enum import StatusEnum
from enums.ui_enum import ElementOperationEnum, DriveTypeEnum
from exceptions.tools_exception import SyntaxErrorError, MysqlQueryIsNullError
@@ -13,6 +13,7 @@ from exceptions.ui_exception import *
from models.socket_model.ui_model import ElementResultModel, ElementModel
from tools.decorator.async_retry import async_retry
from tools.desktop.signal_send import SignalSend
from tools.log_collector import log
from tools.message.error_msg import *
@@ -76,6 +77,7 @@ class ElementMain(WebDevice, AndroidDriver):
elif self.drive_type == DriveTypeEnum.DESKTOP.value:
pass
else:
log.error('不存在的设备类型')
raise Exception('不存在的设备类型')
async def assertion_element(self):
@@ -88,6 +90,7 @@ class ElementMain(WebDevice, AndroidDriver):
elif self.drive_type == DriveTypeEnum.DESKTOP.value:
pass
else:
log.error('不存在的设备类型')
raise Exception('不存在的设备类型')
async def __ope(self, name, ope_type):

View File

@@ -18,6 +18,7 @@ from exceptions.ui_exception import *
from models.socket_model.ui_model import ElementModel, ElementResultModel
from tools.assertion.sql_assertion import SqlAssertion
from tools.message.error_msg import *
from tools.public_methods import sync_global_exception
class AndroidDriver(UiautomatorEquipment,
@@ -60,7 +61,7 @@ class AndroidDriver(UiautomatorEquipment,
except UiObjectNotFoundError as error:
raise ElementLocatorError(*ERROR_MSG_0032, value=(self.element_model.name,), error=error, )
except Exception as error:
print(error)
sync_global_exception(error, False)
else:
if 'locating' in self.element_model.ope_value:
del self.element_model.ope_value['locating']

View File

@@ -4,7 +4,6 @@
# @Time : 2023-09-09 23:17
# @Author : 毛鹏
import time
import uiautomator2
from autotest.ui.base_tools.base_data import BaseData

View File

@@ -29,6 +29,7 @@ class UiautomatorEquipment(BaseData):
def a_swipe_left(self):
"""获取屏幕开关状态"""
self.android.info.get('screenOn')
#
# def a_home(self):
# """返回首页"""

View File

@@ -29,7 +29,7 @@ from tools.data_processor.sql_cache import SqlCache
from tools.desktop.signal_send import SignalSend
from tools.log_collector import log
from tools.message.error_msg import ERROR_MSG_0008, ERROR_MSG_0009, ERROR_MSG_0042, ERROR_MSG_0045, ERROR_MSG_0047
from tools.public_methods import async_global_exception
"""
python -m uiautomator2 init
python -m weditor
@@ -180,7 +180,7 @@ class DriverObject:
await ClientWebSocket.async_send(msg="发送录制接口", func_name=ApiSocketEnum.RECORDING_API.value,
func_args=api_info)
except Exception as error:
log.error(error)
await async_global_exception(error)
async def test_main():

View File

@@ -8,11 +8,10 @@ import asyncio
from autotest.ui.base_tools.driver_object import DriverObject
from autotest.ui.service.cases import CasesMain
from enums.tools_enum import ClientTypeEnum
from enums.ui_enum import DriveTypeEnum
from models.socket_model.ui_model import CaseModel
from service.socket_client import ClientWebSocket
from tools.log_collector import log
from tools.public_methods import async_global_exception
class CaseRun(DriverObject):
@@ -55,9 +54,7 @@ class CaseRun(DriverObject):
log.error('自动化类型不存在,请联系管理员检查!')
await obj.case_page_step()
except Exception as error:
log.error(str(error))
await ClientWebSocket.async_send(code=300,
msg="执行元素步骤时发生未知异常,请检查数据或者联系管理员",
is_notice=ClientTypeEnum.WEB.value)
await async_global_exception(error)
finally:
self.running_tasks -= 1

View File

@@ -15,7 +15,7 @@ from models.socket_model.ui_model import CaseModel, CaseResultModel, PageStepsMo
from service.socket_client import ClientWebSocket
from tools.desktop.signal_send import SignalSend
from tools.log_collector import log
from tools.public_methods import async_global_exception
class CasesMain(StepsMain):
@@ -54,7 +54,7 @@ class CasesMain(StepsMain):
except MangoActuatorError as error:
self.case_result.error_message = f'用例<{self.case_model.name}> 失败原因:{error.msg}'
self.case_result.status = StatusEnum.FAIL.value
log.error(error.msg)
log.warning(error.msg)
break
else:
if page_steps_result_model.status:
@@ -62,17 +62,14 @@ class CasesMain(StepsMain):
else:
self.case_result.error_message = f'用例<{self.case_model.name}> 失败原因:{page_steps_result_model.error_message}'
self.case_result.status = StatusEnum.FAIL.value
log.error(page_steps_result_model.error_message)
log.warning(page_steps_result_model.error_message)
break
await self.case_posterior(self.case_model.posterior_sql)
except MangoActuatorError as error:
self.case_result.error_message = f'用例<{self.case_model.name}> 失败原因:{error.msg}'
self.case_result.status = StatusEnum.FAIL.value
except Exception as error:
log.error(str(error))
await ClientWebSocket.async_send(code=300,
msg="执行元素步骤时发生未知异常,请检查数据或者联系管理员",
is_notice=ClientTypeEnum.WEB.value)
await async_global_exception(error)
else:
msg = self.case_result.error_message if self.case_result.error_message else f'用例<{self.case_model.name}>测试完成'
await ClientWebSocket.async_send(

View File

@@ -73,7 +73,7 @@ class StepsMain(ElementMain):
return self.page_step_result_model
async def __error(self, error: MangoActuatorError):
log.error(
log.warning(
f'元素操作失败element_model{self.element_model.dict()}element_test_result{self.element_test_result.dict()}error{error.msg}')
path = rf'{InitPath.failure_screenshot_file}\{self.element_model.name}{RandomTimeData.get_deta_hms()}.jpg'
SignalSend.notice_signal_c(f'''元素名称:{self.element_test_result.ele_name}

95
MangoActuator/dd.md Normal file
View File

@@ -0,0 +1,95 @@
# ✨️ 概述
DrissionPage 是一个基于 python 的网页自动化工具。
它既能控制浏览器,也能收发数据包,还能把两者合而为一。
可兼顾浏览器自动化的便利性和 requests 的高效率。
它功能强大,内置无数人性化设计和便捷功能。
它的语法简洁而优雅,代码量少,对新手友好。
---
官方网站:[https://drissionpage.cn](https://drissionpage.cn)
<a href='https://gitee.com/g1879/DrissionPage/stargazers'><img src='https://gitee.com/g1879/DrissionPage/badge/star.svg?theme=dark' alt='star'></img></a> <a href='https://gitee.com/g1879/DrissionPage/members'><img src='https://gitee.com/g1879/DrissionPage/badge/fork.svg?theme=dark' alt='fork'></img></a>
项目地址:[gitee](https://gitee.com/g1879/DrissionPage) | [github](https://github.com/g1879/DrissionPage)
您的星星是对我最大的支持💖
---
支持系统Windows、Linux、Mac
python 版本3.6 及以上
支持浏览器Chromium 内核浏览器(如 Chrome 和 Edge)electron 应用
---
# 🛠 如何使用
**📖 使用文档:** [点击查看](https://g1879.gitee.io/drissionpagedocs)
**交流 QQ 群:** 636361957
---
# 💡 理念
简洁而强大!
---
# ☀️ 特性和亮点
作者经过长期实践,踩过无数坑,总结出的经验全写到这个库里了。
## 🎇 强大的自研内核
本库采用全自研的内核,内置无数实用功能,对常用功能作了整合和优化,对比 selenium有以下优点
- 不基于 webdriver
- 无需为不同版本的浏览器下载不同的驱动
- 运行速度更快
- 可以跨`<iframe>`查找元素,无需切入切出
-`<iframe>`看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态,无需切换
- 可以直接读取浏览器缓存来保存图片,无需用 GUI 点击另存
- 可以对整个网页截图包括视口外的部分90以上版本浏览器支持
- 可处理非`open`状态的 shadow-root
## 🎇 亮点功能
除了以上优点,本库还内置了无数人性化设计。
- 极简的语法规则。集成大量常用功能,代码更优雅
- 定位元素更加容易,功能更强大稳定
- 无处不在的等待和自动重试功能。使不稳定的网络变得易于控制,程序更稳定,编写更省心
- 提供强大的下载工具。操作浏览器时也能享受快捷可靠的下载功能
- 允许反复使用已经打开的浏览器。无须每次运行从头启动浏览器,调试超方便
- 使用 ini 文件保存常用配置,自动调用,提供便捷的设置,远离繁杂的配置项
- 内置 lxml 作为解析引擎,解析速度成几个数量级提升
- 使用 POM 模式封装,可直接用于测试,便于扩展
- 高度集成的便利功能,从每个细节中体现
- 还有很多细节,这里不一一列举,欢迎实际使用中体验:)
---
# 🖐🏻 免责声明
禁止将 DrissionPage 应用到任何可能会违反法律规定和道德约束的项目中。
友善使用 DrissionPage遵守蜘蛛协议禁止将 DrissionPage 用于任何可能有损他人的项目中。
如您选择使用 DrissionPage 即代表您遵守此协议,作者不承担任何由于您违反此协议带来任何的法律风险和损失。
同时,作者不对 DrissionPage 可能存在的缺陷导致的损失承担任何责任,一切后果由您承担。
---
# ☕ 请我喝咖啡
如果本项目对您有所帮助,不妨请作者我喝杯咖啡
![](https://gitee.com/g1879/DrissionPageDocs/raw/master/static/img/code.jpg)

View File

@@ -1,85 +0,0 @@
# encoding: utf-8
from PySide6.QtGui import QAction, QIcon, QKeySequence, QShortcut
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QMenu, QSystemTrayIcon
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("最小化系统托盘")
self.resize(400, 300)
# 创建按钮并连接到最小化到系统托盘的方法
self.button = QPushButton("Minimize to Tray")
self.button.clicked.connect(self.minimize_to_tray)
self.setCentralWidget(self.button)
# 初始化系统托盘相关的对象和菜单项
self._restore_action = QAction()
self._quit_action = QAction()
self._tray_icon_menu = QMenu()
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon("trash.png")) # 替换为你的图标路径
self.tray_icon.setToolTip("My Application")
self.create_actions()
self.create_tray_icon()
self.tray_icon.show()
# 连接系统托盘图标的激活事件
self.tray_icon.activated.connect(self.tray_icon_activated)
# 应用程序键盘监听
self.listen_keyboard()
def minimize_to_tray(self):
# 最小化窗口到系统托盘
self.hide()
def restore_from_tray(self):
# 还原窗口
if self.isMinimized():
self.showNormal()
elif self.isMaximized():
self.showMaximized()
else:
self.show()
def tray_icon_activated(self, reason):
# 当系统托盘图标被点击时的处理
if reason == QSystemTrayIcon.ActivationReason.Trigger:
# 如果点击的是触发事件(比如左键单击),则还原窗口
self.restore_from_tray()
def create_actions(self):
# 创建菜单项
self._restore_action = QAction("显示", self)
self._restore_action.triggered.connect(self.restore_from_tray) # "显示"菜单项触发还原窗口的操作
self._quit_action = QAction("退出", self)
self._quit_action.triggered.connect(QApplication.quit) # "退出"菜单项触发退出应用程序的操作
def create_tray_icon(self):
# 创建系统托盘图标和上下文菜单
self._tray_icon_menu = QMenu(self)
self._tray_icon_menu.addAction(self._restore_action)
self._tray_icon_menu.addSeparator()
self._tray_icon_menu.addAction(self._quit_action)
self.tray_icon.setContextMenu(self._tray_icon_menu)
self.tray_icon.show()
def listen_keyboard(self):
# 键盘监听
shortcut = QShortcut(QKeySequence("Esc"), self)
# 当按下 Esc 键时隐藏窗口
shortcut.activated.connect(self.hide)
if __name__ == "__main__":
app = QApplication()
app.setQuitOnLastWindowClosed(False)
window = MyWindow()
window.show()
app.exec()

View File

@@ -8,12 +8,12 @@ from tools.log_collector import log
class MangoActuatorError(Exception):
def __init__(self, code: int, msg: str, value: tuple = None, error: any = None, is_log: bool = True):
def __init__(self, code: int, msg: str, value: tuple = None, error: any = None, is_log: bool = False):
if value:
msg = msg.format(*value)
if error and is_log:
log.error(f'报错提示:{msg} 报错内容:{error}')
else:
log.error(f'报错提示:{msg}')
elif is_log:
log.warning(f'报错提示:{msg}')
self.code = code
self.msg = msg

View File

@@ -9,12 +9,9 @@ from enums.tools_enum import ClientTypeEnum, CacheKeyEnum
from exceptions import MangoActuatorError
from models.socket_model.ui_model import PageStepsModel, WEBConfigModel, CaseModel
from service.socket_client import ClientWebSocket
from settings.settings import GLOBAL_EXCEPTION_CAPTURE
from tools.data_processor.sql_cache import SqlCache
from tools.decorator.convert_args import convert_args
from tools.desktop.signal_send import SignalSend
from tools.log_collector import log
from tools.public_methods import async_global_exception
class UIConsumer:
page_steps: PageSteps = None
@@ -38,14 +35,7 @@ class UIConsumer:
msg=error.msg,
is_notice=ClientTypeEnum.WEB.value)
except Exception as error:
if GLOBAL_EXCEPTION_CAPTURE:
SignalSend.notice_signal_c(f'发送未知异常,请联系管理员!异常类型:{type(error)}')
log.error(f'发送未知异常,请联系管理员!异常类型:{type(error)},错误详情:{str(error)}')
await ClientWebSocket.async_send(code=300,
msg="执行UI步骤时发送未知异常请联系管理员",
is_notice=ClientTypeEnum.WEB.value)
else:
raise error
await async_global_exception(error)
@classmethod
@convert_args(WEBConfigModel)
@@ -60,14 +50,7 @@ class UIConsumer:
cls.page_steps = PageSteps(data.project)
await cls.page_steps.new_web_obj(data)
except Exception as error:
if GLOBAL_EXCEPTION_CAPTURE:
SignalSend.notice_signal_c(f'发送未知异常,请联系管理员!异常类型:{type(error)}')
log.error(f'发送未知异常,请联系管理员!异常类型:{type(error)},错误详情:{str(error)}')
await ClientWebSocket.async_send(code=300,
msg="实例化浏览器对象时,发送未知异常!请联系管理员",
is_notice=ClientTypeEnum.WEB.value)
else:
raise error
await async_global_exception(error)
@classmethod
@convert_args(CaseModel)
@@ -86,12 +69,4 @@ class UIConsumer:
cls.case_run = CaseRun(max_tasks)
await cls.case_run.queue.put(data)
except Exception as error:
if GLOBAL_EXCEPTION_CAPTURE:
SignalSend.notice_signal_c(f'发送未知异常,请联系管理员!异常类型:{type(error)}')
log.error(f'发送未知异常,请联系管理员!异常类型:{type(error)},错误详情:{str(error)}')
await ClientWebSocket.async_send(code=300,
msg="实例化浏览器对象时,发送未知异常!请联系管理员",
is_notice=ClientTypeEnum.WEB.value)
else:
raise error
await async_global_exception(error)

View File

@@ -134,8 +134,8 @@ class ClientWebSocket:
try:
out = json.loads(recv_json)
log.info(f'接收的消息提示:{out["msg"]}')
if out['data']:
log.debug(f"接收的数据:{json.dumps(out['data'], ensure_ascii=False)}")
# if out['data']:
# log.debug(f"接收的数据:{json.dumps(out['data'], ensure_ascii=False)}")
return SocketDataModel(**out)
except json.decoder.JSONDecodeError:
log.error(f'服务器发送的数据不可被序列化,请检查服务器发送的数据:{recv_json}')

View File

@@ -1,12 +0,0 @@
# -*- coding: utf-8 -*-
# @Project: auto_test
# @Description:
# @Time : 2023-08-11 11:08
# @Author : 毛鹏
# ↓-----------------------------------数据源选择-----------------------------------↓
GLOBAL_EXCEPTION_CAPTURE = False
# ↑-----------------------------------数据源选择-----------------------------------↑
if __name__ == '__main__':
print("Global exception capture".upper())

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# @Project: auto_test
# @Description:
# @Time : 2024-04-29 16:27
# @Author : 毛鹏
from enums.tools_enum import ClientTypeEnum
from service.socket_client import ClientWebSocket
from tools.desktop.signal_send import SignalSend
from tools.log_collector import log
async def async_global_exception(error, _is=True):
if _is:
SignalSend.notice_signal_c(f'发送未知异常,请联系管理员!异常类型:{type(error)}')
log.error(f'发送未知异常,请联系管理员!异常类型:{type(error)},错误详情:{str(error)}')
await ClientWebSocket.async_send(code=300,
msg="发生未知异常!请联系管理员",
is_notice=ClientTypeEnum.WEB.value)
else:
raise error
def sync_global_exception(error, _is=True):
if _is:
SignalSend.notice_signal_c(f'发送未知异常,请联系管理员!异常类型:{type(error)}')
log.error(f'发送未知异常,请联系管理员!异常类型:{type(error)},错误详情:{str(error)}')
ClientWebSocket.sync_send(code=300,
msg="发生未知异常!请联系管理员",
is_notice=ClientTypeEnum.WEB.value)
else:
raise error

View File

@@ -20,8 +20,8 @@ BASE_DIR = Path(__file__).resolve().parent.parent
mysql_db_name = 'mango_server'
mysql_user = 'root'
# mysql_password = 'root'
mysql_password = 'mP123456&'
mysql_password = 'root'
# mysql_password = 'mP123456&'
mysql_ip = 'localhost'
# mysql_ip = '36.213.11.72'
mysql_port = 3306

View File

@@ -47,13 +47,9 @@
* 所有功能介绍
![功能演示.gif](功能演示.gif)
* UI用例执行过程及测试报告
![UI执行演示.gif](功能演示.gif)
* API用例执行过程及测试报告
![API执行演示.gif](功能演示.gif)
#### 加作者微信,进芒果自动化测试群(记得备注git芒果测试平台。否则可能会不通过哦)
如果觉得项目对你有帮助,那就请我喝咖啡!
<div style="display:flex;">
<img src="img_8.png" style="width:45%;">
<div style="width:10%"></div>
<img src="微信.jpg" style="width:45%;">
</div>
![微信.gif](功能演示.gif)

BIN
UI执行演示.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 MiB

BIN
img_8.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 129 KiB