异常捕获
常见的异常类型
代码执行顺序从上到下依次运行的,如果出错了,后面的代码不会出错。 --所以要对异常做处理。
常见的异常的类型,不需要记;平时写代码的时候 经常会报错,积累常见错误,排查问题。
常见异常的报错的类型:NameError,IndexError,KeyError,ValueError,ZeroDivisionError
1、NameError: name ‘a’ is not defined
2、IndexError: list index out of range
3、KeyError: ‘nam’
4、ValueError: invalid literal for int() with base 10: ‘b’
5、ZeroDivisionError: division by zero
对于自动化编程人员来说,代码报错了怎么排查?–具备基本能力。
- 1、点击文件跳转到报错行数–看代码除了什么 + 结合报错信息【异常类型+原因】
- 2、debug 调试: 单步 【直接单步【不会进入函数里】 + step into【进入函数执行】】
== 不推荐print
– 日志记录
# NameError: name 'a' is not defined
# a变量没有运行,异常类型:NameError
# print(a)
#IndexError: list index out of range
# 列表索引超过范围:异常类型 IndexError
a = [1,2,3]
print(a[100])
# KeyError: 'passwd'
# 字典的key错误了,key不存在的,KeyError
# b = {"name":"程程"}
# print(b["passwd"])
#ValueError: invalid literal for int() with base 10: 'b'
# 值错误: int转化不了一个字符串里面是字母的。 ValueError
# a = "b"
# print(int(a))
# ZeroDivisionError: division by zero
# 除数不能为0 : 异常类型: ZeroDivisionError
# print(1/0)
异常捕获
异常捕获:我的代码如果报错了 不会向下继续运行了,后面代码白写了。当代码报错了时候,需要进行处理。
– 先捕获错误
- 然后再处理后续操作。
问题1: 如何去捕获错误?
- 第一步: 先找到代码报错的行数是哪一行?
- 第二步: 加一个代码捕获: try
语法1:
try:
执行的可能会报错代码 如果报错了,会执行except里面的代码;
如果没有报错,正常执行try语句,except里不会执行。
except:
发生了错误,处理代码
这个语句执行完成之后,再执行后续的代码。继续向下运行其他的代码。不会中断代码运行。
问题: 怎么判断代码是否可能会报错呢?这样的代码我才需要加上try语句。
- 数据不是由程序员控制代码 就会容易出现异常
- 1) 用户输入数据, 加上异常不会
- 2)函数参数传参进来,变量用户控制的
- 3)数据是由其他的函数运行后的结果生成的 拿到了返回值 正常运行,但是返回值没拿到,就会报错了。
- 4)断言错误,影响到用例是否通过的结果。
语法2:
try:
执行的可能会报错代码 如果报错了,会执行except里面的代码;
如果没有报错,正常执行try语句,except里不会执行。
except Exception as err:
# 捕获了这个异常并且把这个异常信息存在一个变量里。err
发生了错误,处理代码
# 语法1:
# a = [1,2,3]
#
# try:
# # 可能会报错的代码,先运行try语句里的代码; 如果报错了,会执行except里面的代码;
# print(a[1])
# except:
# # 发生了错误,处理代码
# print("索引错误!")
#
# print("其他的后续代码...")
# 语法2:
a = [1,2,3]
try:
# 可能会报错的代码,先运行try语句里的代码; 如果报错了,会执行except里面的代码;
print(a[100])
except Exception as err: # 捕获了这个异常并且把这个异常信息存在一个变量里。err
# 发生了错误,处理代码
print(f"索引错误!:{err}")
print("其他的后续代码...")
异常的进阶用法
语法1:
try:
执行的可能会报错代码 如果报错了,会执行except里面的代码;
如果没有报错,正常执行try语句,except里不会执行。
except:
发生了错误,处理代码
这个语句执行完成之后,再执行后续的代码。继续向下运行其他的代码。不会中断代码运行。
语法2:
try:
执行的可能会报错代码 如果报错了,会执行except里面的代码;
如果没有报错,正常执行try语句,except里不会执行。
except Exception as err: # 万能捕获
# 捕获了这个异常并且把这个异常信息存在一个变量里。err
发生了错误,处理代码
语法3: 捕获不同的类型异常 做不同的操作处理。【错误1-日志; 错误2-发送邮件,错误3-警告】
try:
执行的可能会报错代码 如果报错了,会执行except里面的代码;
如果没有报错,正常执行try语句,except里不会执行。
如果try里的错误类型匹配except后的异常类型 才会捕获 不然不会。仍然执行try里面的语句,会终止代码运行。
except 异常类型 as err:
# 捕获了这个异常并且把这个异常信息存在一个变量里。err
发生了错误,处理代码
总结: 针对不同的异常类型去做异常异捕获比较麻烦。我们可以直接用万能捕获。
- 如果代码量不大 项目不负责可以用万能匹配 但是不推荐。
- 代码量大了 项目更加要求精细时候,我们区分异常类型 分别做捕获 分别不同的处理。
- 推荐:先做不同类型捕获 然后再来万能的兜底。
# a = [1,2,3]
# try:
# # 可能会报错的代码,先运行try语句里的代码; 如果报错了,会执行except里面的代码;
# print(a[100])
# except IndexError as err: # 捕获了这个异常并且把这个异常信息存在一个变量里。err
# # 发生了错误,处理代码
# print(f"索引错误!:{err}")
# print("其他的后续代码...")
# a = [1,2,3]
# try:
# # 可能会报错的代码,先运行try语句里的代码; 如果报错了,会执行except里面的代码;
# print(a[100])
# except KeyError as err: # 捕获了这个异常并且把这个异常信息存在一个变量里。err
# # 发生了错误,处理代码
# print(f"索引错误!:{err}")
# print("其他的后续代码...")
a = [1,2,3]
b = {"name":"程程"}
try:
# 可能会报错的代码,先运行try语句里的代码; 如果报错了,会执行except里面的代码;
print(a[1]) #如果这行代码代码出错,从这个位置跳转到except了,后面的语句不会执行。
key = b["passwd"] # 这个代码可以运行 但是因为异常类型不对 没有捕获成功。
except IndexError as err: # 捕获了这个异常并且把这个异常信息存在一个变量里。err
# 发生了错误,处理代码
print(f"索引错误!:{err}")
except KeyError as e:
print(f"key错误{e}")
except Exception as err: # 万能兜底
print("代码执行错误!")
print("其他的后续代码...")
手动捕获异常
语法1:
try:
执行的可能会报错代码 如果报错了,会执行except里面的代码;
如果没有报错,正常执行try语句,except里不会执行。
except:
发生了错误,处理代码
这个语句执行完成之后,再执行后续的代码。继续向下运行其他的代码。不会中断代码运行。
语法2:
try:
执行的可能会报错代码 如果报错了,会执行except里面的代码;
如果没有报错,正常执行try语句,except里不会执行。
except Exception as err: # 万能捕获
# 捕获了这个异常并且把这个异常信息存在一个变量里。err
发生了错误,处理代码
语法3: 捕获不同的类型异常 做不同的操作处理。【错误1-日志; 错误2-发送邮件,错误3-警告】
try:
执行的可能会报错代码 如果报错了,会执行except里面的代码;
如果没有报错,正常执行try语句,except里不会执行。
如果try里的错误类型匹配except后的异常类型 才会捕获 不然不会。仍然执行try里面的语句,会终止代码运行。
except 异常类型 as err:
# 捕获了这个异常并且把这个异常信息存在一个变量里。err
发生了错误,处理代码
语法4: raise 主动抛出异常
代码本身没有问题,没有错误; 但是程序员主动抛出异常,让代码自动终止运行。
报错了 中断代码运行了,后续的代码不会运行了。
什么场景下会要求主动抛出异常呢? --理解
案例: 测试用例执行时候,针对每一个用例做断言【预期结果 vs 执行结果】 如果不一样,断言失败了;
因为测试用例结果记录到日志里;捕获断言的异常,记录到日志;
但是因为捕获了,这个用例断言不会失败了,报告统计里【pytest】不会认为是失败了。
这种场景下: 先捕获了异常,做日志记录操作; 然后再raise抛出这个异常,让测试用例失败显示在报告里。
# def add(a,b):
# return a + b
#
# print(add(1,2))
# print(add(-1,-3))
# 需求: 不能输入负数 【用户名:不让输入特殊字符】,遇到负数之后,主动异常了。
def add(a,b):
if a < 0 or b<0:
raise ValueError("不能为负数") # 主动抛出异常,raise关键字
return a + b
print(add(1,2))
print(add(-1,-3)) # 报错了 中断代码运行了,后续的代码不会运行了。
print("后续的代码执行...")
运行结果如下:
Traceback (most recent call last):
File "D:\BaiduNetdiskDownload\1-Python基础\20231115_py65基础第八节课-异常捕获和日志处理\day08_异常捕获和日志\d4_手动抛出异常.py", line 54, in <module>
print(add(-1,-3)) # 报错了 中断代码运行了,后续的代码不会运行了。
File "D:\BaiduNetdiskDownload\1-Python基础\20231115_py65基础第八节课-异常捕获和日志处理\day08_异常捕获和日志\d4_手动抛出异常.py", line 50, in add
raise ValueError("不能为负数") # 主动抛出异常,raise关键字
ValueError: 不能为负数
断言
断言: 预期结果和执行结果比对 判断测试是否通过。
- 1、if 预期结果 == 执行结果
- 2、 assert断言
assert语法:
1、assert 执行结果 == 预期结果
2、如果断言失败了 就会报错 AssertionError异常类型。会终止后面的代码运行
3、加提示信息,会显示再结果里。 只有断言失败了 会显示。 断言成功了,提示不会显示。
4、在项目里。因为断言可能会失败 所以一般会在断言前后加上try语句 异常捕获;
- 抓到报错之后 记录日志
- 还要主动抛出异常 为了让用例显示失败。
actural = 6
expected = 5
try:
assert actural == expected,"断言失败了,5=!6"
except AssertionError as err:
print(f"断言失败{err}") #记录到日志
raise err
print("566666")
finally的使用
finally语法: 写在try except语句。
- 捕获了异常执行
- 没有捕获也会执行
actural = 6
expected = 5
try:
assert actural == expected,"断言失败了,5=!6"
except AssertionError as err:
print(f"断言失败{err}") #记录到日志
raise err
finally:
print("566666")
日志
日志: 平时测试工作有没有看过日志么?
- 开发项目的里日志,服务器里收集日志 :Linux命令 tail -f
- app里收集日志 :adb logcat
- 日志查看原因目的是什么.?
== 日志是代码记录的信息,写代码的人会把代码过程中步骤或者关键信息记录到日志
== 方便除了问题 通过日志查找问题。
== 任何完整框架【开发项目】 都应该有日志系统。== 自动化框架也有日志。
对于写代码的人 都需要设计日志进入代码里。
日志级别:
- TRACE: 废话 基本不用
- DEBUG:用于调试程序,很详细信息 变量值的,于主体业务功能没有关系,在线上环境里没有的
- INFO: 用于记录日常信息,主体业务功能信息 ==最多显示
- WARNING: 警告信息,触发犯错的边缘
- ERROR: 犯错了 出错了 异常了 断言失败 == 用的比较多
- CRITICAL: 严重错误信息,崩溃了 无法继续运行了 致命错误
优先级: critical > error > waring > info > debug
自动化测试的日志功能库:
- logging:自带库
- loguru : 第三方的库 == 我们用这个 因为更方便 代码简单。
- 安装: pip install loguru ,如果安装失败了 因为切换国内源
- 导入: from loguru import logger
现在的做法日志打印再在控制台的,最终项目的日志肯定要写到文件里。-- 生成日志文件的。
from loguru import logger
# 打印日志 info 级别的日志
logger.info("日常操作日志")
# 断言失败了 记录日志 error日志
actural = 6
expected = 5
try:
assert actural == expected,"断言失败了,5=!6"
except AssertionError as err:
logger.error(f"断言失败{err}") #记录到日志
raise err
print("566666")
日志文件
现在的做法日志打印再在控制台的,最终项目的日志肯定要写到文件里。-- 生成日志文件的。
add()方法,参数如下:
- sink:文件名字
- 追加模式 持续写入到这个文件里
- 运行再这个目录里会生成对应的文件 : 以后会做了代码分层之后,路径处理 --pathlib
- encoding: 编码格式,如果要记录除了英文之外的字符 设置UTF8编码
- level: 设置的级别及其优先级以上的级别 都会被记录到日志文件里。
- 优先级: critical > error > waring > info > debug
- rotation:文件分割:很多系统每天生成一个日志文件。 按照一定规则进行分割。
- 文件大小: 100MB 超过了 新创建一个继续记录日志
- 时间分割: 23:59: 每天晚上23:59新创建一个继续记录日志
- 按照天 week 1 week, 10 day
- retention: 日志文件的数量,如果文件数量超过设置的数量,老日志会删除。
- 20 : 最多存储20个日志文件。 多了删除老的。
项目的代码加日志和要求: 最好详细到每一个操作。
from loguru import logger
# 写入日志文件 在最上方 =入口文件里写 run.py 最新执行
logger.add(sink="mytest.log",
encoding="UTF8",
level="INFO",
rotation="1kB",
retention= 20)
# 登录用例 -- 记录日志信息 加logger
username = input("请输入用户名:")
password = input("请输入密码:")
logger.info(f"输入的用户名是:{username},输入密码是:{password}")
logger.info("进行用户登录操作..")
if username == "admin" and password == "123456":
msg = "登录成功"
logger.info("登录成功")
else:
msg = "登录失败"
logger.error("登录失败!")
运行结果如下:
请输入用户名:andy
请输入密码:12345
2024-03-21 15:53:35.025 | INFO | __main__:<module>:31 - 输入的用户名是:andy,输入密码是:12345
2024-03-21 15:53:35.029 | INFO | __main__:<module>:33 - 进行用户登录操作..
2024-03-21 15:53:35.029 | ERROR | __main__:<module>:40 - 登录失败!
mytest.log的内容如下:
2023-11-15 22:10:29.916 | INFO | __main__:<module>:33 - 进行用户登录操作..
2023-11-15 22:10:29.919 | ERROR | __main__:<module>:40 - 登录失败!
2024-03-21 15:53:35.025 | INFO | __main__:<module>:31 - 输入的用户名是:andy,输入密码是:12345
2024-03-21 15:53:35.029 | INFO | __main__:<module>:33 - 进行用户登录操作..
2024-03-21 15:53:35.029 | ERROR | __main__:<module>:40 - 登录失败!
标签:try,err,loguru,python,代码,except,assert,报错,日志
From: https://blog.csdn.net/qq_35283902/article/details/136910850