python unittest 接口自动化遇到的问题记录

1. ConfigParser类读取config.ini的options全部返回为小写字母

查看configparser.ConfigParser类中,是因为optionxform方法返回了optionstr.lower()

 def optionxform(self, optionstr):
        return optionstr.lower()

第一种方法是直接修改源码,把return optionstr.lower() 改成return optionstr.
此种解决方案的不足之处是只能在本机生效,换台机器就会出问题

第二种方法是自己封装一个MyConfig类,继承ConfigParser,重写optionxform方法
(方案参考https://blog.csdn.net/Ha_hha/article/details/78965011)

from configparser import ConfigParser
import os


CONF = os.path.join(os.path.dirname(__file__), "../config/config.ini")


class MyConfig(ConfigParser):
    # 继承ConfigParser,解决读取options自动转换成小写的问题
    def __init__(self):
        super().__init__()

    def optionxform(self, optionstr):
        # 重写optionxform方法,解决ConfigParser类读取的options都会转换成小写的问题
        return optionstr


config = MyConfig()
# logger.debug("以utf-8编码方式打开配置文件{}".format(conf))
config.read(filenames=CONF, encoding="utf-8")


if __name__ == "__main__":
    print(config.sections())
    print(config.options("LOG"))

2. 日志打印多次

自己封装的日志模块,在多个地方都有调用,用于输出日志,运行runner时发现日志有4条重复记录,百度后发现是因为在多个模块中实例化自定义的log类,每次实例化就会addhandler一次,有4处实例化就会addhandler4次,导致日志输出4次

# filename: log.py
import logging


class MyLogger:
    def __new__(self):
        # 1. 创建log收集器,设置日志收集级别
        mylogger = logging.getLogger("mylogger")
        mylogger.setLevel(config.get("LOG", "LOG_LEVEL"))

        # 2. 创建输出渠道,设置日志输出级别
        sh = logging.StreamHandler()
        fh = logging.FileHandler(config.get("LOG", "FILENAME"), mode="w", encoding="utf-8")
        sh.setLevel(config.get("LOG", "SH_LEVEL"))
        fh.setLevel(config.get("LOG", "FH_LEVEL"))

        # 3. 设置文件输出格式,添加格式
        fmt = logging.Formatter(config.get("LOG", "LOG_FORMAT", raw=True))   # 读取包含%符号时,避免被转义,增加一个参数raw=True
        fh.setFormatter(fmt)

        # 4. 把输出渠道添加到日志收集器
        mylogger.addHandler(sh)
        mylogger.addHandler(fh)
        return mylogger

其他调用log模块的模块有4个,其中1个代码如下:

# filename: operate_excel.py 
import openpyxl
import os
from prac_unittest_3.common.log import MyLogger   # 导入MyLogger类

logger = MyLogger()  # 每个调用log模块的地方都要实例化一个logger对象

class CaseObj:
    def __init__(self, zip_obj):
        for i in list(zip_obj):
            setattr(self, i[0], i[1])


# 1. 封装一个读取excel的类
class ReadExcel:
    def __init__(self, filename, sheetname):
        self.filename = filename
        self.sheetname = sheetname
        self.wb = openpyxl.load_workbook(self.filename)
        self.sh = self.wb[self.sheetname]
        logger.debug("打开文件{}的{}表单".format(self.filename, self.sheetname))

    def read_data_obj(self):
        cases = []
        # self.sh.rows 是一个生成器,不能通过下标来访问,self.sh.rows转换成list先,再通过list下标去访问.
        rows = list(self.sh.rows)
        # 1. 读取第一行作为表头,存放在title列表
        titles = [cell.value for cell in rows[0]]
        logger.debug("读取第一行:{}".format(titles))

        # 2. 读取第2--max_row,存放在data列表
        for row in rows[1:]:
            datas = [cell.value for cell in row]
            logger.debug("读取到的数据为:{}".format(datas))
            # 3. 聚合title和data,形成字典形式
            case_obj = CaseObj(zip(titles, datas))
            logger.debug("组装成用例:{}".format(case_obj))
            # 4. 每条用例追加到总的用例列表中
            cases.append(case_obj)
        return cases

    def write_data(self, row, column, data):
        logger.debug("第{}行第{}列写入的内容为:{}".format(row, column, data))
        self.sh.cell(row=row, column=column).value = data
        logger.debug("保存内容到文件{}".format(self.filename))
        self.wb.save(self.filename)

修改log模块,先实例化一个logger对象,其他需要用到日志模块的,直接导入这个logger对象即可

# filename: log.py
import log
from prac_unittest_3.common.config import config

LOG_LEVEL = config.get("LOG", "LOG_LEVEL")
FILENAME = config.get("LOG", "FILENAME")
SH_LEVEL = config.get("LOG", "SH_LEVEL")
FH_LEVEL = config.get("LOG", "FH_LEVEL")
FMT = config.get("LOG", "LOG_FORMAT", raw=True)


class MyLogger:
    def __new__(self):
        # 1. 创建log收集器,设置日志收集级别
        mylogger = logging.getLogger("mylogger")
        mylogger.setLevel(LOG_LEVEL)

        # 2. 创建输出渠道,设置日志输出级别
        sh = logging.StreamHandler()
        fh = logging.FileHandler(FILENAME, mode="w", encoding="utf-8")
        sh.setLevel(SH_LEVEL)
        fh.setLevel(FH_LEVEL)

        # 3. 设置文件输出格式,添加格式
        fmt = logging.Formatter(FMT)   # 读取包含%符号时,避免被转义,增加一个参数raw=True
        fh.setFormatter(fmt)

        # 4. 把输出渠道添加到日志收集器
        mylogger.addHandler(sh)
        mylogger.addHandler(fh)
        return mylogger

logger = MyLogger()   # 在log模块中实例化MyLogger类,其他需要用到log模块的地方直接import这个logger对象即可。实现只需实例化一次即可到处调用

operate_excel.py 只需修改2行代码,import MyLogger改为import logger,把实例化MyLogger类去掉

# filename: operate_excel.py 
import openpyxl
import os
from prac_unittest_3.common.log import logger   # 修改为直接导入log模块的logger对象,直接调用logger.debug() 就可以输出日志


class CaseObj:
    def __init__(self, zip_obj):
        for i in list(zip_obj):
            setattr(self, i[0], i[1])


# 1. 封装一个读取excel的类
class ReadExcel:
    def __init__(self, filename, sheetname):
        self.filename = filename
        self.sheetname = sheetname
        self.wb = openpyxl.load_workbook(self.filename)
        self.sh = self.wb[self.sheetname]
        logger.debug("打开文件{}的{}表单".format(self.filename, self.sheetname))

    def read_data_obj(self):
        cases = []
        # self.sh.rows 是一个生成器,不能通过下标来访问,self.sh.rows转换成list先,再通过list下标去访问
        rows= list(self.sh.rows)
        # 1. 读取第一行作为表头,存放在title列表
        titles = [cell.value for cell in rows[0]]
        logger.debug("读取第一行:{}".format(titles))

        # 2. 读取第2--max_row,存放在data列表
        for row in rows[1:]:
            datas = [cell.value for cell in row]
            logger.debug("读取到的数据为:{}".format(datas))
            # 3. 聚合title和data,形成字典形式
            case_obj = CaseObj(zip(titles, datas))
            logger.debug("组装成用例:{}".format(case_obj))
            # 4. 每条用例追加到总的用例列表中
            cases.append(case_obj)
        return cases

    def write_data(self, row, column, data):
        logger.debug("第{}行第{}列写入的内容为:{}".format(row, column, data))
        self.sh.cell(row=row, column=column).value = data
        logger.debug("保存内容到文件{}".format(self.filename))
        self.wb.save(self.filename)

2.封装log模块,从配置文件中读取日志格式参数报错

LOG_FORMAT= "%(asctime)s %(filename)s line:%(lineno)d %(levelname)s: %(message)s",由于含有百分号%导致无法读取
configparser.InterpolationMissingOptionError: Bad value substitution: option 'LOG_FORMAT' in section 'LOG' contains an interpolation key 'asctime' which is not a valid option name. Raw value: '"%(asctime)s %(filename)s line:%(lineno)d %(levelname)s: %(message)s"'

C:\Users\MACHENIKE\AppData\Local\Programs\Python\Python37\python.exe "D:/Documentss/WeChat Files/wxid_t7usl9w03ogs22/FileStorage/File/2023-02/训练营资料(1)/day01/prac_unittest_3/common/log.py"
Traceback (most recent call last):
 File "D:/Documentss/WeChat Files/wxid_t7usl9w03ogs22/FileStorage/File/2023-02/训练营资料(1)/day01/prac_unittest_3/common/log.py", line 34, in <module>
   logger = MyLogger()
 File "D:/Documentss/WeChat Files/wxid_t7usl9w03ogs22/FileStorage/File/2023-02/训练营资料(1)/day01/prac_unittest_3/common/log.py", line 25, in __new__
   fmt = logging.Formatter(config.get("LOG", "LOG_FORMAT"))   # 读取包含%符号时,避免被转义,增加一个参数raw=True
 File "C:\Users\MACHENIKE\AppData\Local\Programs\Python\Python37\lib\configparser.py", line 799, in get
   d)
 File "C:\Users\MACHENIKE\AppData\Local\Programs\Python\Python37\lib\configparser.py", line 394, in before_get
   self._interpolate_some(parser, option, L, value, section, defaults, 1)
 File "C:\Users\MACHENIKE\AppData\Local\Programs\Python\Python37\lib\configparser.py", line 434, in _interpolate_some
   option, section, rawval, var) from None
configparser.InterpolationMissingOptionError: Bad value substitution: option 'LOG_FORMAT' in section 'LOG' contains an interpolation key 'asctime' which is not a valid option name. Raw value: '"%(asctime)s  %(filename)s line:%(lineno)d %(levelname)s: %(message)s"'

Process finished with exit code 1

代码:

# filename: log.py
import log
from prac_unittest_3.common.config import config

LOG_LEVEL = config.get("LOG", "LOG_LEVEL")
FILENAME = config.get("LOG", "FILENAME")
SH_LEVEL = config.get("LOG", "SH_LEVEL")
FH_LEVEL = config.get("LOG", "FH_LEVEL")
FMT = config.get("LOG", "LOG_FORMAT")


class MyLogger:
    def __new__(self):
        # 1. 创建log收集器,设置日志收集级别
        mylogger = logging.getLogger("mylogger")
        mylogger.setLevel(LOG_LEVEL)

        # 2. 创建输出渠道,设置日志输出级别
        sh = logging.StreamHandler()
        fh = logging.FileHandler(FILENAME, mode="w", encoding="utf-8")
        sh.setLevel(SH_LEVEL)
        fh.setLevel(FH_LEVEL)

        # 3. 设置文件输出格式,添加格式
        fmt = logging.Formatter(FMT)   
        fh.setFormatter(fmt)

        # 4. 把输出渠道添加到日志收集器
        mylogger.addHandler(sh)
        mylogger.addHandler(fh)
        return mylogger


logger = MyLogger()


if __name__ == "__main__":
    logger.debug("------------debug")
    logger.error("------------error")

只需在读取config.get("LOG","LOG_FORMAT",raw=True)变量时,增加一个raw=True属性即可,修改为

# filename: log.py
import log
from prac_unittest_3.common.config import config

LOG_LEVEL = config.get("LOG", "LOG_LEVEL")
FILENAME = config.get("LOG", "FILENAME")
SH_LEVEL = config.get("LOG", "SH_LEVEL")
FH_LEVEL = config.get("LOG", "FH_LEVEL")
FMT = config.get("LOG", "LOG_FORMAT", raw=True)   # 读取包含%符号时,避免被转义,增加一个参数raw=True


class MyLogger:
    def __new__(self):
        # 1. 创建log收集器,设置日志收集级别
        mylogger = logging.getLogger("mylogger")
        mylogger.setLevel(LOG_LEVEL)

        # 2. 创建输出渠道,设置日志输出级别
        sh = logging.StreamHandler()
        fh = logging.FileHandler(FILENAME, mode="w", encoding="utf-8")
        sh.setLevel(SH_LEVEL)
        fh.setLevel(FH_LEVEL)

        # 3. 设置文件输出格式,添加格式
        fmt = logging.Formatter(FMT)   
        fh.setFormatter(fmt)

        # 4. 把输出渠道添加到日志收集器
        mylogger.addHandler(sh)
        mylogger.addHandler(fh)
        return mylogger


logger = MyLogger()

if __name__ == "__main__":
    logger.debug("------------debug")
    logger.error("------------error")
posted @ 2023-03-11 11:28  2019勇往直前  阅读(101)  评论(0编辑  收藏  举报