软件测试/测试开发丨Python 装饰器常见的报错信息、原因和解决方案

Python 装饰器简介

装饰器(Decorator)是 Python 非常实用的一个语法糖功能。装饰器本质是一种返回值也是函数的函数,可以称之为“函数的函数”。其目的是在不对现有函数进行修改的情况下,实现额外的功能。

在 Python 中,装饰器属于纯粹的“语法糖”,不使用也没关系,但是使用的话能够大大简化代码,使代码更加简洁易读。

最近在霍格沃兹测试学院的《Python 测试开发实战进阶》课程中学习了 App 自动化测试框架的异常处理,存在一定重复代码,正好可以当作题材,拿来练习一下装饰器。

装饰器学习资料,推荐参考 RealPython

realpython.com/primer-on-p…

本文主要汇总记录 Python 装饰器的常见踩坑经验,列举报错信息、原因和解决方案,供大家参考。

装饰器避坑指南

坑 1:Hint: make sure your test modules/packages have valid Python names.

报错信息
test_market.py:None (test_market.py)
ImportError while importing test module 'D:\project\Hogwarts_11\test_appium\testcase\test_market.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test_market.py:9: in <module>from test_appium.page.app import App
..\page\app.py:12: in <module>from test_appium.page.base_page import BasePage
..\page\base_page.py:16: in <module>from test_appium.utils.exception import exception_handle
..\utils\exception.py:11: in <module>from test_appium.page.base_page import BasePage
E   ImportError: cannot import name 'BasePage' from 'test_appium.page.base_page' (D:\project\Hogwarts_11\test_appium\page\base_page.py)

原因

exception.py 文件和 base_page.py 文件之间存在相互调用关系。

解决方案

把循环调用的包引入信息放在函数内。只要一方的引用信息放在函数里即可,不必两边都放。

我只在 exception.py 文件里改了,base_page.py 保持不变。

exception.py
def exception_handle(func):def magic(*args, **kwargs):# 防止循环调用报错from test_appium.page.base_page import BasePage# 获取BasePage实例对象的参数self,这样可以复用driver_self: BasePage = args[0]
...

坑 2:IndexError: tuple index out of range

报错信息
test_search.py:None (test_search.py)
test_search.py:11: in <module>from test_appium.page.app import App
..\page\app.py:12: in <module>from test_appium.page.base_page import BasePage
..\page\base_page.py:52: in <module>class BasePage:
..\page\base_page.py:74: in BasePagedef find(self, locator, key=None):
..\page\base_page.py:50: in exception_handlereturn magic()
..\page\base_page.py:24: in magic_self: BasePage = args[0]
E   IndexError: tuple index out of range

原因

第一次写装饰器真的很容易犯这个错,一起来看下哪里写错了。

def decorator(func):def magic(*args, **kwargs):_self: BasePage = args[0]...return magic(*args, **kwargs)# 这里的问题!!!不应该返回函数调用,要返回函数名称!!!return magic()  

为什么返回函数调用会报这个错呢?

因为调用 magic() 函数的时候,没有传参进去,但是 magic() 里面引用了入参,这时 args 没有值,自然就取不到 args[0] 了。

解决方案

去掉括弧就好了。

def decorator(func):def magic(*args, **kwargs):_self: BasePage = args[0]...return magic(*args, **kwargs)# 返回函数名,即函数本身return magic

坑 3:异常处理只执行了1次,自动化无法继续

报错信息

主要是定位元素过程中出现的各种异常,NoSuchElementExceptionTimeoutException等常见问题。

原因

异常处理后,递归逻辑写得不对。return func() 执行了 func(),跳出了异常处理逻辑,所以异常处理只执行一次。

正确的写法是 return magic()

感觉又是装饰器小白容易犯的错误 …emmm…. :no_mouth:

解决方案

为了直观,已过滤不重要代码,异常处理逻辑代码会在文末放出。

def exception_handle(func):def magic(*args, **kwargs):_self: BasePage = args[0]try:return func(*args, **kwargs)# 弹窗等异常处理逻辑except Exception as e:for element in _self._black_list:elements = _self._driver.find_elements(*element)if len(elements) > 0:elements[0].click()# 异常处理结束,递归继续查找元素 # 这里之前写成了return func(*args, **kwargs),所以异常只执行一次!!!!!return magic(*args, **kwargs)raise ereturn magic

坑 4:如何复用 driver?

问题

自己刚开始尝试写装饰器的时候,发现一个问题。

装饰器内需要用到 find_elements,这时候 driver 哪里来?还有 BasePage 的私有变量 error_max 和 error_count 怎么获取到呢?创建一个 BasePage 对象?然后通过 func 函数来传递 driver ?

func 的 driver 是私有的,不能外部调用(事实证明可以emmm…)。

我尝试把异常相关的变量做成公共的,没用,还是无法解决 find_elements 的调用问题。

解决方案

思寒老师的做法是,在装饰器里面创建一个 self 变量,取 args[0],即函数 func 的第一个入参self

_self: BasePage = args[0] 这一简单的语句成功解答了我所有的疑问。

类函数定义里面 self 代表类自身,因此可以获取 ._driver 属性,从而调用 find_elements。

坑 5:AttributeError

找到元素后,准备点击的时候报错

报错信息
EINFO:root:('id', 'tv_search')
INFO:root:None
INFO:root:('id', 'image_cancel')
INFO:root:('id', 'tv_agree')
INFO:root:('id', 'tv_search')
INFO:root:Nonetest setup failed
self = <test_appium.testcase.test_search.TestSearch object at 0x0000018946B70940>def setup(self):
>       self.page = App().start().main().goto_search()test_search.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _self = <test_appium.page.main.MainPage object at 0x0000018946B70780>def goto_search(self):
>       self.find(self._search_locator).click()
E       AttributeError: 'NoneType' object has no attribute 'click'..\page\main.py:20: AttributeError

原因

看了下 find 函数,找到元素后,有返回元素本身。

    @exception_handledef find(self, locator, key=None):logging.info(locator)logging.info(key)# 定位符支持元组格式和两个参数格式locator = locator if isinstance(locator, tuple) else (locator, key)WebDriverWait(self._driver, 10).until(expected_conditions.visibility_of_element_located(locator))element = self._driver.find_element(*locator)return element

那就是装饰器写得不对了:

def exception_handle(func):def magic(*args, **kwargs):_self: BasePage = args[0]try:# 这里只是执行了函数,但是没有returnfunc(*args, **kwargs)# 弹窗等异常处理逻辑except Exception as e:raise ereturn magic

解决方案

要在装饰器里面返回函数调用,要不然函数本身的返回会被装饰器吃掉。

def exception_handle(func):def magic(*args, **kwargs):_self: BasePage = args[0]try:# return函数执行结果return func(*args, **kwargs)# 弹窗等异常处理逻辑except Exception as e:raise ereturn magic

思考:

写装饰器的时候,各种return看着有点头晕。每个函数里面都可以return,分别代表什么含义呢???

def exception_handle(func):def magic(*args, **kwargs):_self: BasePage = args[0]try:# 第1处 return:传递func()函数的返回值。如果不写,原有return则失效return func(*args, **kwargs)# 弹窗等异常处理逻辑except Exception as e:for element in _self._black_list:elements = _self._driver.find_elements(*element)if len(elements) > 0:elements[0].click()# 异常处理结束,递归继续查找元素 # 第2处 return:递归调用装饰后的函数。magic()表示新函数,func()表示原函数,不可混淆return magic(*args, **kwargs)raise e# 第3处 return:返回装饰后的函数,装饰器语法。不能返回函数调用magic()return magic

装饰器完整实现
exception.py
import logginglogging.basicConfig(level=logging.INFO)def exception_handle(func):def magic(*args, **kwargs):# 防止循环调用报错from test_appium.page.base_page import BasePage# 获取BasePage实例对象的参数self,这样可以复用driver_self: BasePage = args[0]try:# logging.info('error count is %s' % _self._error_count)result = func(*args, **kwargs)_self._error_count = 0# 返回调用函数的执行结果,要不然返回值会被装饰器吃掉return result# 弹窗等异常处理逻辑except Exception as e:# 如果超过最大异常处理次数,则抛出异常if _self._error_count > _self._error_max:raise e_self._error_count += 1for element in _self._black_list:# 用find_elements,就算找不到元素也不会报错elements = _self._driver.find_elements(*element)logging.info(element)# 是否找到弹窗if len(elements) > 0:# 出现弹窗,点击掉elements[0].click()# 弹窗点掉后,重新查找目标元素return magic(*args, **kwargs)# 弹窗也没有出现,则抛出异常logging.warning("no error is found")raise ereturn magic

一点学习心得

“纸上得来终觉浅,绝知此事要躬行”。遇到问题后尝试自主解决,这样踩过的坑才印象深刻。

所以,建议大家最好先根据自己的理解写一遍装饰器,遇到问题实在没有头绪了,再参考思寒老师的解法,那时会有一种豁然开朗的感觉,这样学习的效果最好。

以上,Python 装饰器踩到的这些坑,如有遗漏,欢迎补充~

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/580611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ueditor富文本编辑器中图片上传地址配置以及抓取远程图片地址的配置

一&#xff1a;图片上传保存地址配置 打开文件ueditor.php,找到imagePathFormat进行修改即可 一&#xff1a;远程抓取图片配置 打开文件ueditor.config.js,找到catchRemoteImageEnable&#xff0c;取消注释即可

2024年元旦节放假通知

致尊敬的客户以及全体同仁&#xff1a; 旧岁已展千重锦&#xff0c;新年再进百尺竿。在这辞旧迎新之际&#xff0c;易天光通信提前祝您元旦快乐&#xff01;生意兴隆&#xff0c;身体健康&#xff0c;万事如意&#xff01;根据国家法定假期的规定&#xff0c;并结合公司实际情…

开源verilog模拟 iverilog verilator +gtkwave仿真及一点区别

开源的 iverilog verilator 和商业软件动不动几G几十G相比&#xff0c;体积小的几乎可以忽略不计。 两个都比较好用&#xff0c;各有优势。 iverilog兼容性好。 verilator速度快。 配上gtkwave 看波形&#xff0c;仿真工具基本就齐了。 说下基本用法 计数器 counter.v module…

【接口测试】Postman(一)--接口测试知识准备 _

1.0 前言 ​ 应用程序编程接口&#xff08;Application Programming Interface, API&#xff09;是这些年来最流行的技术之一&#xff0c;强大的Web应用程序和领先的移动应用程序都离不开后端强大的API。API技术的应用给系统开发带来了便利&#xff0c;但也对测试人员提出了更高…

VMware16 pro 安装openEuler-23.09-x86_64,详细操作流程+详图。

1.环境&#xff1a; win11, vmware16 pro, openEuler-23.09-x86_64-dvd.iso 社区版openEuler 23.09官方下载地址&#xff1a; openEuler下载 | 欧拉系统ISO镜像 | openEuler社区官网欧拉操作系统(openEuler, 简称“欧拉”)是面向数字基础设施的操作系统,支持服务器、云计算、…

【教学类-35-07】17号的字帖(二)班级字帖“大4”(A4竖版1份)

作品展示 背景需求&#xff1a; 大4班17号孩子练习数字书写&#xff0c;上一次是“17”号&#xff0c;这次是大“4”。 【教学类-35-05】17号的学号字帖&#xff08;A4竖版1份&#xff09;-CSDN博客文章浏览阅读440次&#xff0c;点赞6次&#xff0c;收藏7次。【教学类-35-05…

Vue2从源码角度来回答一些常见的问题

1.请说一下Vue2响应式数据的理解&#xff08;先知道基本的问题在哪里&#xff0c;源码的角度来回答&#xff0c;用的时候会有哪些问题&#xff09; 可以监控一个数据的修改和获取操作。针对对象格式会给每个对象的属性进行劫持 Object.defineProperty 源码层面 initData ->…

在香橙派5 Plus上搭建Gitlab

作为一个码农&#xff0c;一定知道Github这个最大的成人交友网站。但是Github在国内不稳定&#xff0c;经常拉不下来代码&#xff0c;也就无法推送代码。为了更方便的使用&#xff0c;顺便更好地了解Git工具&#xff0c;决定在香橙派5 Plus上搭建一个属于自己的代码仓库。 1、…

k8s的二进制部署: 源码包部署-----node节点部署

服务器IP软件包k8s--master0120.0.0.61kube-aplserver&#xff0c;kube-controer-manager&#xff0c;kube-scheduler&#xff0c;etcdk8s--master0220.0.0.62kube-controer-manager&#xff0c;kube-schedulernode节点0120.0.0.62kubelet&#xff0c;kube-proxy&#xff0c;et…

【问题系列】同时管理多版本node方案

目录 一、问题描述 二、解决方案 三、详细步骤 3.1 安装NVM&#xff1a; 3.2 运行NVM 3.3 安装Node.js 3.4 切换Node.js版本 3.5 验证安装&#xff1a; 四、拓展 4.1 设置默认 Node.js 版本 4.2 列出已安装的 Node.js 版本 4.3 其他命令介绍 一、问题描述 需要运行…

C#实现串口通讯

1、官网下载Launch Virtual Serial Port Driver Virtual Serial Port Driver - create and emulate virtual COM port&#xff0c;开个虚拟串口&#xff1a; Pair模式&#xff08;一对&#xff0c;成双成对的意思&#xff0c;就是COM1向COM2传或者COM2向COM1,好比两台机器的CO…

软件工程期末复习

● 用例&#xff1a;借书 ●参与者&#xff1a;管理员,借阅者 ●操作流&#xff1a; ① 管理员进入图书借阅界面&#xff0c;用例开始。 ② 系统要求输入借阅者的借书证编码。 ③系统检验借书证编码,如果正确,则显示借阅者的信息。 A1&#xff1a;借书证编码有错。 A2: 如果该借…

巨量引擎大众消费发布“十佳好课”榜单:涵盖五大行业 助力商家进阶

2023&#xff0c;中国消费市场出现温和复苏态势&#xff0c;在不确定市场环境下&#xff0c;大众消费商家如何用好科学营销手段&#xff0c;实现确定性增长?如何紧跟平台趋势&#xff0c;把握生意增长先机? 为了给商家持续提供前沿适配的营销方法、解决实际生意卡点&#xff…

MYSQL一一函数一一字符串函数

嘿嘿大家好我回来啦&#xff0c;今天我们要学习的是MYSQL中的函数&#xff0c;函数呢我们又分为字符串函数&#xff0c;数值函数&#xff0c;日期函数&#xff0c;流程函数来介绍&#xff0c;今天重点介绍字符串函数(从小题到案例方便你们更加深入的理解) 函数指的是一段可以直…

软件测试面试题合集,金三银四offer稳了。。。

前言 前面看到了一些面试题&#xff0c;总感觉会用得到&#xff0c;但是看一遍又记不住&#xff0c;所以我把面试题都整合在一起&#xff0c;都是来自各路大佬的分享&#xff0c;为了方便以后自己需要的时候刷一刷&#xff0c;不用再到处找题&#xff0c;今天把自己整理的这些…

nginx源码分析-1

使用gdb查看函数上下文&#xff1a; gdb attach nginx的work线程 监听端口状态时&#xff1a; 断点打在ngx_http_process_request 并通过浏览器触发请求时&#xff1a;

在linux下添加中文输入

一共三个步骤&#xff0c;过程中有参考Linux最详细且好用的安装中文输入法教程 - 知乎 1. 打开左侧的software下载安装Fcitx 2. 点击左下侧搜索language support&#xff0c;把keyboard input method system改成Fcitx 4 3. input source把中文放在最前面 成功&#xff0…

基于openGauss5.0.0全密态数据库等值查询小案例

基于openGauss5.0.0全密态数据库等值查询小案例 一、全密态数据库简介二、环境说明三、测试步骤四、使用约束 一、全密态数据库简介 价值体现&#xff1a; 密态数据库意在解决数据全生命周期的隐私保护问题&#xff0c;使得系统无论在何种业务场景和环境下&#xff0c;数据在传…

Web 3.0 是什么

第 1 章 明晰Web 3.0 从本章开始,就进入了本书的第一篇章,入门Web3.0,在第一篇章中将会让读者对Web3.0有一个整体的认知,为学习后面的章节打下基础。 在本章中,主要介绍的是Web的发展历史,包涵Web1.0、Web2.0、Web3.0的发展过程,以及资本为什么需要入场Web3.0、Web3.0…

遗传算法的应用——求解一元函数的极值

遗传算法的应用——求解一元函数的极值 1 基本概念2 预备知识3.1 模拟二进制转化为十进制的方法3.2 轮盘赌选择算法 3 问题4 Matlab代码5 运行效果6 总结 1 基本概念 遗传算法(Genetic Algorithm,GA)是模拟生物在自然环境中遗传和进化过程从而形成的随机全局搜索和优化方法&am…