Python自动化测试实战:日志记录与异常处理详解
本文深入解析Python自动化测试中的两大核心机制:日志记录与异常处理。通过详细代码示例,演示如何利用logging模块实现多级别日志输出、文件回滚及多模块共享配置,并掌握try-except-finally结构进行健壮的错误捕获与自定义异常类设计,助力提升测试用例的稳定性与可维护性。
Python自动化测试日志与异常处理概述
本文通过具体实例,详细解析Python自动化测试中异常捕获与日志记录的操作方法,供读者参考学习。
为确保自动化测试用例具备足够的健壮性,有效捕获并处理异常、准确记录日志对于掌握测试执行状态至关重要。下文将详细介绍在自动化测试场景中应用日志与异常处理的具体用法。
日志模块(logging)应用详解
一、日志模块基础
输出日志是程序开发中的重要环节,清晰的日志有助于快速排查程序运行状态。Python标准库提供了强大的logging模块。需注意,Logger对象不应直接实例化,这一设计带来了诸多便利,下文将逐步展开讲解。Logging模块主要提供两种记录日志的方式。
- 方式一:使用模块级别的函数记录日志
模块级函数记录日志
import logging
#设置日志,包括filename、level、format、filemode、stream,其中format属性极其丰富,详情可查看API文档,这里只做简要介绍
logging.basicConfig(level = logging.INFO,
format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt = "%Y/%m%d %H%M%S",
filename = "log.txt")
#消息级别,五级
logging.debug("芹泽多摩雄")
logging.info("真")
logging.warning("男")
logging.error("人")
logging.critical("!")
- 方式二:基于日志系统四大组件(日志器、处理器、过滤器、格式器)的方式记录日志
基于四大组件的日志配置
import logging
# 生成日志实例,日志器
logger = logging.getLogger(__name__)
#基本单元的配置(LEVER)
logger.setLevel(level = logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
#生成管道分支,处理器
handler_1 = logging.FileHandler("log.txt")
handler_2 = logging.StreamHandler()
#自定义格式,格式器
handler_1.setFormater(formatter, “%Y-%m-%d %H:%M:%S”)
handler_2.setFormater(formatter, “%Y-%m-%d %H:%M:%S”)
#对接分支管道与源头,处理器
logger.addHandler(handler_1)
logger.addHandler(handler_2)
#层级结构,logger的名称是一个以'.'分割的层级结构,每个'.'后面的logger都是'.'前面的logger的children,通常配合过滤器一起使用
#过滤器
#。。。。保留
#开始记录
logger.debug("芹泽多摩雄")
logger.info("真")
logger.warning("男")
logger.error("人")
logger.critical("!")
- 观察代码结构可以发现,logging模块有一个核心的日志处理主对象,其他处理方式均通过addHandler方法添加进去。例如,采用logging.StreamHandler可将日志输出至控制台(流),或使用FileHandler将日志写入文件。
- 日志回滚机制
日志回滚与多模块共享配置
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)
#定义一个RotatingFileHandler,最多备份3个日志文件,每个日志文件最大1K
rHandler = RotatingFileHandler("log.txt",maxBytes = 1*1024,backupCount = 3)
rHandler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
rHandler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)
logger.addHandler(rHandler)
logger.addHandler(console)
logger.debug("芹泽多摩雄")
logger.info("真")
logger.warning("男")
logger.error("人")
logger.critical("!")
- 多模块环境下的日志应用
#主模块
import logging
import subModule
logger = logging.getLogger("mainModule")
logger.setLevel(level = logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)
logger.addHandler(handler)
logger.addHandler(console)
#子模块
import logging
module_logger = logging.getLogger("mainModule.sub")
class SubModuleClass(object):
def __init__(self):
self.logger = logging.getLogger("mainModule.sub.module")
值得注意的是,Logger的命名非常关键。若在主模块中定义了名为'mainModule'的Logger并进行配置,子模块可通过命名规则共享父Logger的定义与配置。父子Logger通过名称识别,任何以'mainModule'开头的Logger(如'mainModule.sub')均被视为其子Logger。
- 尽管支持继承配置或自定义配置,但在大型项目中直接管理仍略显繁琐。实际应用中,常采用JSON或YAML格式封装日志配置,加载配置文件即可应用日志设置。具体操作将在后续内容中分解,此处先展示一个综合实例。
封装日志工具类与清理机制
# -*- coding: utf-8 -*-
__author__ = 'Secret608'
import logging
import time
import os
import re
class Log(object):
def __init__(self, loggerName):
'''
进行日志初始化,包括存储路径、名称、级别、调用文件等
'''
#基本属性
self.logger = logging.getLogger(loggerName)
self.logger.setLevel(logging.WARNING)
#特有属性(文件地址+日志记录格式)
rq = time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time()))
log_path = os.path.join(os.path.dirname(os.getcwd()), 'logs')
log_title = os.path.join(log_path, loggerName + '_'+ rq) + ".log"
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log = logging.FileHandler(log_title)
log.setFormatter(formatter)
#加到基本属性中,得到一个完整的初始化对象
self.logger.addHandler(log)
def getLog(self):
return self.logger
def delLog(self, fileName):
log_path = os.path.join(os.path.dirname(os.getcwd()), 'logs')
regexp = re.compile('^'+fileName+'s.*')
filelist = os.listdir(log_path)
try:
[os.remove(os.path.join(log_path, i)) for i in filelist if regexp.match(i) == None]
except WindowsError:
pass
else:
return "ok"
if __name__ == "__main__":
a = Log("hah")
a.delLog("hah")
异常处理机制与最佳实践
二、异常处理机制
- 异常类型分类
- 内置异常:Python拥有强大的异常处理能力,内置了多种异常类,可向用户精确反馈错误信息。在Python中,异常也是对象,可对其进行操作。BaseException是所有内置异常的基类,但用户自定义类通常不直接继承BaseException,而是继承自Exception类,且所有内置异常类均在exceptions模块中定义。
- 自定义异常:开发者可通过创建新的异常类来定义专属异常,该类应直接或间接继承自Exception。例如,创建一个名为MyError的类,基类为Exception,用于在异常触发时输出更丰富的信息。
- 异常捕获流程
- 当异常发生时,需对异常进行捕获并执行相应处理。Python中常用的异常捕获结构为try...except...,将可能出错的代码置于try模块中,用except处理异常。每个try块必须至少对应一个except块。此外,与Python异常相关的关键字主要包括:try/except、pass、as(用于定义异常实例)、else、finally、raise。
- 捕获所有异常示例:
异常捕获结构与自定义异常
# -*- coding: utf-8 -*-
#异常处理的语法:
try:
#执行可能出现异常的语句
except '异常名字':
#出现异常执行的语句
else:
#执行没有出现异常的语句
finally:
#异常与否都执行的语句
#demo
try:
a = 0
b = 1
c = b/a
print(c)
except ZeroDivisionError:
print("分母不能为0")
except NameError:
print("名称错误")
except (ZeroDivisionError, NameError):
print("你的分母等于0或者变量名不存在")
except Exception as e:
print("你的变量名或者分母值确实没错,但是出现了其他的错误,详见%s" %e)
d = 1
finally:
print("听说没有错误?不能忍,反正我要让你难受!")
if d == 1:
raise ValueError("你有其它错误")
else:
raise FuckError("开不开心?")
#咦? FuckError是啥?貌似没有定义啊,这里定义一波,届时位置移动到前面去
class FuckError(Exception):
def __int__(self,*args,**keargs):
super(FuckError,self).__int__(*args,**keargs)#python2
self.args = args
print(args)
更多异常类型可查阅官方API文档(https://docs.python.org/3/library/exceptions.html#base-classes)。
更多关于Python相关内容的感兴趣读者,可查看本站专题:《Python日志操作技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》及《Python文件与目录操作技巧汇总》。
希望本文所述内容能对大家的Python程序设计有所帮助。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









