zoukankan      html  css  js  c++  java
  • 004_python常用模块

    day13 - day 15

    1. 日志相关项:

         1> 在代码中添加日志,然后输出到文件中;

         2> 用于记录代码逻辑执行过程,当报错异常时用于分析问题;

         3> 定义日志收集器:要从代码当中按照要求,收集对应的日志,并输出到渠道当中;

              a> 要收集哪些级别以上的日志?

              b> 日志以什么样的格式显示?

              c> 日志要输出到哪里去?

          4> 日志级别(Level): debug 调试 -  info 基本信息 -  warning 警告 - error 报错 - critical(FATA) 严重错误 

          5> 日志显示的格式(Formatter):时间,日志级别,代码文件,第几行,信息

          6> 日志输出渠道(Handle):文件(FileHandle)、控制台(StreamHandle)

    2. logging模块:

         1> 有一个默认的日志收集器:root,可以自己设定

         2> 收集的是warning及warning以上级别的日志

         3> 日志格式:日志级别  收集器的名字   输出的内容

         4> 输出渠道:控制台/文件

     1 import logging
     2 
     3 
     4 class Student:
     5 
     6     identify = "student"
     7 
     8     def __init__(self,name,age,sex,cn_score,math_score,en_score):
     9         self.name = name
    10         self.age = age
    11         self.sex = sex
    12         self.cn_score = cn_score
    13         self.math_score = math_score
    14         self.en_score = en_score
    15         logging.info("hello,logging!!!")
    16 
    17     def get_sum_of_score(self):
    18         sum = self.cn_score + self.math_score + self.en_score
    19         logging.debug("{} 的语数外总分为:{}".format(self.name, sum))
    20         return sum
    21 
    22     def get_avg_of_score(self):
    23         avg = self.get_sum_of_score()/3
    24         logging.warning("{} 的语数外平均分为:{}".format(self.name, avg))
    25 
    26     def print_stu_info(self):
    27         logging.warning("我的名字叫{},年龄:{},性别:{}".format(self.name,self.age,self.sex))
    28 
    29 bing = Student("",20,"",80, 88, 77)
    30 bing.get_sum_of_score()
    31 bing.get_avg_of_score()
    logging实例

    3. 定制输出日志

         1> 设置日志收集对象: logger = logging.getLogger(日志名字)

         2> 设置日志级别: logger.setLevel(日志级别),一般为INFO

         3> 设置日志格式:

               a> 自定义日志格式:fmt_str = "%(asctime)s %(name)s %(levelname)s %(filename)s [%(lineno)d] %(message)s"

               b> 实例化一个日志格式类:formatter = logging.Formatter(fmt_str)   

         4> 指定日志输出渠道 - 控制台:

               a> 输出到控制台(渠道):stream_handle = logging.StreamHandler() 

               b> 设置渠道当中的日志显示格式:stream_handle.setFormatter(formatter)   

         5> 指定日志输出到文件:stream_handle = logging.StreamHandler()  file_handle = logging.FileHandler(文件名)

               a> 输出到文件(渠道):file_handle = logging.FileHandler(文件名) 

               b> 设置渠道当中的日志显示格式:file_handle .setFormatter(formatter) 

               c> 设置从收集的日志中获取错误日志到文件中:

                      file_handle2 = logging.FileHandler('mylog_eeor.txt',encoding='utf-8')

                      file_handle2.setFormatter(formatter)

                      file_handle2.setLevel('ERROR')  # 指定输入到mylog_error.txt文件中的日志级别为ERROR以上级别

         6> 将渠道与日志收集器绑定起来:logger.addHandler(stream_handle )  或  logger.addHandler(file_handle )  或  logger.addHandler(file_handle2)

     1 import logging
     2 
     3 # 第一步: 设置日志收集器
     4 logger = logging.getLogger('自动化')
     5 
     6 # 第二步:设置日志级别
     7 logger.setLevel('INFO')  # 设置能收集的日志级别
     8 
     9 # 第三步:设置日志格式
    10 fmt_str = "%(asctime)s %(name)s %(levelname)s %(filename)s [%(lineno)d] %(message)s"
    11 formatter = logging.Formatter(fmt_str)
    12 
    13 # 第四步 第1种:指定日志渠道 - 控制台
    14 stream_handle = logging.StreamHandler()
    15 stream_handle.setFormatter(formatter)
    16 # 第四步 第2种:指定日志渠道 - 文件
    17 file_handle = logging.FileHandler('mylog.txt',encoding='utf-8')
    18 file_handle.setFormatter(formatter)
    19 
    20 # 设置专门收集错误日志
    21 file_handle2 = logging.FileHandler('mylog_eeor.txt',encoding='utf-8')
    22 file_handle2.setFormatter(formatter)
    23 file_handle2.setLevel('ERROR')  # 指定输入到mylog_error.txt文件中的日志级别为ERROR以上级别
    24 
    25 # 第五步 第1种: 渠道与日志收集器绑定
    26 logger.addHandler(stream_handle)
    27 # 第五步 第2种: 渠道与日志收集器绑定
    28 logger.addHandler(file_handle)
    29 logger.addHandler(file_handle2)
    30 
    31 # 第六步: 定制的日志应用于输出日志信息
    32 if __name__ == "__main__":
    33 
    34     logger.info('这是自己定制的基本日志信息')
    35     logger.warning('这是一条警告信息')
    36     logger.error('这是一条错误信息')
    37     logger.critical('这是一条严重错误的信息')
    定制日志

    运行结果中mylog_error.txt的日志如下

    4. 滚动输出日志:日志文件按大小或时长定时生成几个日志文件存放日志 (from logging import handlers)

        1> handlers.RotatingFileHandler('my_rotate_log',maxBytes=1,backupCount=10,encoding="utf-8")

              a> maxBytes:设置每个日志文件大小为1byte,按日志文件大小控制文件的生成;

              b> backupCount:设置最多备份保留最新的日志文件个数;

        2> handlers.TimedRotatingFileHandler('my_rotate_time_log.txt',when='S',backupCount=2,encoding="utf-8")

             a> when:设置日志文件按时长生成,默认按 h 小时生成,可以设置的值有S,M,H,D,W{0-6}

             b> backupCount:设置最多备份保留最新的日志文件个数;

     1 import logging
     2 import time
     3 from logging import handlers
     4 
     5 # 第一步: 设置日志收集器
     6 logger = logging.getLogger('自动化')
     7 
     8 # 第二步:设置日志级别
     9 logger.setLevel('INFO')
    10 
    11 # 第三步:设置日志格式
    12 fmt_str = "%(asctime)s %(name)s %(levelname)s %(filename)s [%(lineno)d] %(message)s"
    13 formatter = logging.Formatter(fmt_str)
    14 
    15 # # 第四步 第1种:指定日志渠道 - 控制台
    16 stream_handle = logging.StreamHandler()
    17 stream_handle.setFormatter(formatter)
    18 
    19 # 第四步:指定日志渠道 - 文件
    20 # file_handle = logging.FileHandler('mylog.txt',encoding='utf-8')
    21 # file_handle = handlers.RotatingFileHandler('my_rotate_log.txt',maxBytes=2,backupCount=3,encoding="utf-8")
    22 file_handle = handlers.TimedRotatingFileHandler('my_rotate_time_log.txt',when='S',backupCount=2,encoding="utf-8")
    23 file_handle.setFormatter(formatter)
    24 
    25 # # 第五步 第1种: 渠道与日志收集器绑定
    26 logger.addHandler(stream_handle)
    27 # 第五步 第2种: 渠道与日志收集器绑定
    28 logger.addHandler(file_handle)
    29 
    30 # 第六步: 定制的日志应用于输出日志信息
    31 logger.info('11')
    32 time.sleep(2)
    33 logger.warning('22')
    34 logger.error('33')
    35 logger.error('44')
    36 logger.error('55')
    滚动生成日志

    5. 封装日志类

         1> 封装日志类后,在业务代码逻辑中增加日志记录代码执行过程,以及报错时根据日志分析问题;

         2> 继承Logger类,不用自己再实现 info() debug()  error() warning() critical()方法;

         3> 封装的日志类,应用于实际业务场景;

     1 import logging
     2 from  logging import Logger
     3 
     4 class MyLoger(Logger): # 继承Logger类,不用自己实现 info() debug() error()等方法
     5 
     6     def __init__(self,name,level=logging.INFO,filename = None):
     7         # 1. 设置日志收集者名字,日志级别
     8         super().__init__(name,level)
     9         # 2. 设置日志输出渠道 (控制台和文件)
    10         fmt_str = "%(asctime)s %(name)s %(levelname)s %(filename)s [%(lineno)d] %(message)s"
    11         formatter = logging.Formatter(fmt_str)
    12 
    13         stream_handle = logging.StreamHandler()
    14         stream_handle.setFormatter(formatter)
    15         self.addHandler(stream_handle)  # 绑定日志收集器
    16         if filename:
    17             file_handle = logging.FileHandler(filename, encoding='utf-8')
    18             file_handle.setFormatter(formatter)
    19             self.addHandler(file_handle) # 绑定日志收集器
    myloger
     1 """
     2 第4题 :定义类并实例化对象
     3 定义一个登录的测试用例类LoginCase。每一个实例对象都是一个登陆测试用例。 
     4  属性:用例名称 预期结果 实际结果(初始化时不确定值哦) 
     5 方法: 
     6       1) 运行用例 
     7          说明:有2个参数:用户名和密码。 能够登录成功的用户名:py37, 密码:666666。 
     8          通过用户名和密码正确与否来判断,是否登录成功。并返回实际结果。 
     9          ps: 可以在用例内部考虑处理不符合的情况哦:密码长度不是6位/密码不正确/用户名不正确等。。    
    10       2) 获取用例比对结果(比对预期结果和实际结果是否相等,并输出成功or失败) 
    11  实例化2个测试用例 ,并运行用例(调用方法) ,呈现用例结果(调用方法)
    12 """
    13 from python基本语法.day15_封装日志模块.mylogger import MyLoger
    14 loger = MyLoger('py37',filename='auto.log')  # 实例化
    15 
    16 
    17 class LoginCase:
    18 
    19     def __init__(self,case_name,case_expected):
    20         loger.info('创建一条测试用例,用例名称为:{}'.format(case_name))
    21         self.case_name = case_name
    22         self.case_expected = case_expected
    23         self.case_actual = None
    24 
    25     def run_case(self,user,passwd):
    26         loger.info('用例数据为:用户名 {},密码 {}'.format(user,passwd))
    27         if len(passwd) != 6:
    28             self.case_actual = "密码长度不等于6"
    29             return
    30         if user != "py37":
    31             self.case_actual = "用户名错误"
    32             return
    33         if passwd != "666666":
    34             self.case_actual = "密码错误"
    35             return
    36         if user == "py37" and passwd == "666666":
    37             self.case_actual = "登陆成功"
    38         loger.info('用例运行完成,运行结果为:{}'.format(self.case_actual))
    39 
    40     def case_res(self):
    41         loger.info('开始结果比对')
    42         loger.info("实际结果为:{}".format(self.case_actual))
    43         loger.info("预期结果为:{}".format(self.case_expected))
    44         if self.case_actual != self.case_expected:
    45             # print("实际结果与预期结果不相等,用例不通过!")
    46             loger.error('实际结果与预期结果不相等,用例不通过!')
    47         else:
    48             # print("恭喜,实际结果与预期结果相等,用例通过!")
    49             loger.info('恭喜,实际结果与预期结果相等,用例通过!')
    50 
    51 login_suc = LoginCase("登陆成功","登陆成功")
    52 login_suc.run_case("py37","666668")
    53 login_suc.case_res()
    54 
    55 login_failed = LoginCase("登陆失败","密码错误")
    56 login_failed.run_case("py37","666688")
    57 login_failed.case_res()
    日志类应用

    6. 单例模式:整个运行过程中,只有一个实例化对象,如上面的日志类使用单例模式更好

     1 import logging
     2 from  logging import Logger
     3 
     4 class MyLoger(Logger): # 继承Logger类,不用自己实现 info() debug() error()等方法
     5 
     6     def __init__(self,name,level=logging.INFO,filename = None):
     7         # 1. 设置日志收集者名字,日志级别
     8         super().__init__(name,level)
     9         # 2. 设置日志输出渠道 (控制台和文件)
    10         fmt_str = "%(asctime)s %(name)s %(levelname)s %(filename)s [%(lineno)d] %(message)s"
    11         formatter = logging.Formatter(fmt_str)
    12 
    13         stream_handle = logging.StreamHandler()
    14         stream_handle.setFormatter(formatter)
    15         self.addHandler(stream_handle)  # 绑定日志收集器
    16         if filename:
    17             file_handle = logging.FileHandler(filename, encoding='utf-8')
    18             file_handle.setFormatter(formatter)
    19             self.addHandler(file_handle) # 绑定日志收集器
    20 
    21 loger = MyLoger('py37',filename='auto.log')  # 实例化一个日志对象,该模块被导入后,直接使用logger调用方法
    myloger
     1 # from python基本语法.day15_封装日志模块.mylogger import MyLoger
     2 # loger = MyLoger('py37',filename='auto.log')  # 实例化
     3 
     4 # 更好的方法,因为在mylogger模块统一实例化
     5 from python基本语法.day15_封装日志模块.mylogger import loger
     6 
     7 class LoginCase:
     8 
     9     def __init__(self,case_name,case_expected):
    10         loger.info('创建一条测试用例,用例名称为:{}'.format(case_name))
    11         self.case_name = case_name
    12         self.case_expected = case_expected
    13         self.case_actual = None
    14 
    15     def run_case(self,user,passwd):
    16         loger.info('用例数据为:用户名 {},密码 {}'.format(user,passwd))
    17         if len(passwd) != 6:
    18             self.case_actual = "密码长度不等于6"
    19             return
    20         if user != "py37":
    21             self.case_actual = "用户名错误"
    22             return
    23         if passwd != "666666":
    24             self.case_actual = "密码错误"
    25             return
    26         if user == "py37" and passwd == "666666":
    27             self.case_actual = "登陆成功"
    28         loger.info('用例运行完成,运行结果为:{}'.format(self.case_actual))
    29 
    30     def case_res(self):
    31         loger.info('开始结果比对')
    32         loger.info("实际结果为:{}".format(self.case_actual))
    33         loger.info("预期结果为:{}".format(self.case_expected))
    34         if self.case_actual != self.case_expected:
    35             # print("实际结果与预期结果不相等,用例不通过!")
    36             loger.error('实际结果与预期结果不相等,用例不通过!')
    37         else:
    38             # print("恭喜,实际结果与预期结果相等,用例通过!")
    39             loger.info('恭喜,实际结果与预期结果相等,用例通过!')
    40 
    41 login_suc = LoginCase("登陆成功","登陆成功")
    42 login_suc.run_case("py37","666668")
    43 login_suc.case_res()
    44 
    45 login_failed = LoginCase("登陆失败","密码错误")
    46 login_failed.run_case("py37","666688")
    47 login_failed.case_res()
    日志类应用

    7. 配置文件的读取 (ConfigParser类 -- .ini,PyYaml模块 -- .yaml)

         1> 创建一个.ini的配置文件:[section] option=value,文件中可以用 # 或 ; 来增加注释信息

          2> python读取.ini配置文件数据

               a> 导入ConfigParser类

               b> 实例化ConfigParser类,:conf = ConfigParser() 

               c> 调用read()方法,读取整个配置文件的内容到内存中:conf.read(filename,encoding="utf-8")

               e> 读取配置文件中指定的section的option,读取出来默认是字符串:name = conf.get('log','name')

               f> 支持读取出来为 bool int float: conf.getboolean(section,option)   conf.getint(section,option)   conf.getfloat(section,option)

    conf.ini配置文件内容如下:

    [log]
    name=py37
    level=INFO
    filename=auto.log

    [mysql]
    host=XXX
    database=XXX
    port=3306
    user=XXX
    passwd=XXX
    name=XX

    ;注释信息
    #注释信息

    from configparser import ConfigParser
    
    conf = ConfigParser()
    # 读取整个配置文件的内容到内存中
    conf.read('conf.ini',encoding='utf-8')
    
    # 读取配置文件中指定的section的option,读取出来默认为字符串
    name = conf.get('log','name')
    # print(name)
    
    # 读取出来为int,port需要是整数。还有getboolean()  getfloat()
    port = conf.getint('mysql','port')
    # print(port)

          3> 封装ConfigParser类,用于其他模块可以读取配置文件,如上面日志类的实例化时,日志收集者的名字,日志级别和日志文件都写死了,怎么实现配置化呢?

    1 from configparser import ConfigParser
    2 
    3 class MyConf(ConfigParser):
    4 
    5     def __init__(self,filename):
    6         super().__init__()
    7         self.read(filename, encoding="utf-8")  # 一定要先读取配置文件后,才能再使用myconf.get()读取具体option的值
    myconfig
     1 import logging
     2 from  logging import Logger
     3 
     4 from python基本语法.day15_封装日志模块.myconfig import MyConf
     5 
     6 class MyLoger(Logger): # 继承Logger类,不用自己实现 info() debug() error()等方法
     7 
     8     # def __init__(self,name,level=logging.INFO,filename = None):
     9     def __init__(self):
    10         # 让日志的收集者名字,日志级别和日志文件名可配置
    11         myconf = MyConf('conf.ini')  # 实例化配置类
    12 
    13         name = myconf.get('log', 'name')
    14         level = myconf.get('log', 'level')
    15         filename = myconf.get('log', 'filename')
    16 
    17         # 1. 设置日志收集者名字,日志级别
    18         super().__init__(name,level)
    19         # 2. 设置日志输出渠道 (控制台和文件)
    20         fmt_str = "%(asctime)s %(name)s %(levelname)s %(filename)s [%(lineno)d] %(message)s"
    21         formatter = logging.Formatter(fmt_str)
    22 
    23         stream_handle = logging.StreamHandler()
    24         stream_handle.setFormatter(formatter)
    25         self.addHandler(stream_handle)  # 绑定日志收集器
    26         if filename:
    27             file_handle = logging.FileHandler(filename, encoding='utf-8')
    28             file_handle.setFormatter(formatter)
    29             self.addHandler(file_handle) # 绑定日志收集器
    30 
    31 
    32 # loger = MyLoger('py37',filename='auto.log')  # 实例化一个日志对象,该模块被导入后,直接使用loger调用方法
    33 
    34 loger = MyLoger() #日志相关参数配置在.ini文件中了,故不需要传参数了
    myloger

     配置类 --> 日志类 --> 业务代码:配置类给到了日志类使用,日志类给到了业务代码使用;

    PS: 一定要先读取配置文件后,才能再使用myconf.get()读取具体option的值

    8. ConfigParser类的写入:set/write

        1> 在已有section下添加/修改option的value: conf.set(section,option,value)

        2> 若要新增section:conf.add_section(section)

        3> 将1>和2>中的变更写入到配置文件当中:conf.write(open(文件, 'w', encoding='utf-8'))

    from configparser import ConfigParser
    
    conf = ConfigParser()
    
    # 给log下写入test=001
    conf.set('log','test','001')
    
    # 新增section -- dev
    conf.add_section('dev')
    
    # 最后将变更写入配置文件中
    conf.write(open('conf.ini','w'))

    9. Yaml文件的特点

          1> 以数据为中心,使用空白,缩进,分行组织数据,从而使得数据更加简洁易懂;

          2> 大小写敏感;

          3> 使用缩进表示层级关系 - ;

          4> 禁止使用tab缩进,只能使用空格键;

          5> 缩进长度没有限制,只要元素对齐就表示这些元素属于一个层级;

          6> 使用#表示注释;

          7> 字符串可以不用引号标注;  

    10. yaml文件的3种数据结构:

         1> 字典:使用冒号(:)表示键值对,注意冒号后面有一个空格;

         2> 列表:使用连字符(-)表示,注意连字符后面有一个空格;

         3> 纯量scalar:字符串,数字,布尔值,不可变数据类型;

     11. 操作yaml文件

           1> 第三方库:pyyaml模块  pip install pyyaml

            2> 从yaml中读取数据

                a> 导入yaml: import yaml

                b> 打开yaml文件:open(yaml文件路径, encoding='utf-8')

                c> 加载文件:s = yaml.load(fs,yaml.FullLoader)

    import yaml
    
    with open('conf.yaml',encoding='utf-8') as fs:
        s = yaml.load(fs,yaml.FullLoader)
        print(s)

    13.  unittest的基本用法

           1> TestCase:编写测试用例类时需要继承该类,用于编辑测试用例;

           2> setUp:用于实现每条测试用例的前置条件;

           3> tearDown:用于实现每条测试用例的后置条件;

           4> setUpClass:通过@classmethod装饰成类方法,用于实现每个测试类中所有测试用例的前置条件;

           5> tearDownClass:通过@classmethod装饰成类方法,用于实现每个测试类中所有测试用例的后置条件;

     1 import unittest
     2 
     3 class TestDemo(unittest.TestCase):
     4     @classmethod
     5     def setUpClass(cls) -> None:
     6         print("***这是该测试类中所有用例执行前的前置条件***")
     7     def setUp(self) -> None:
     8         print("---这是每条测试用例的前置条件---")
     9 
    10     def test_01_login(self):
    11         print("用例1:这是一条登录测试用例")
    12 
    13     def test_01_register(self):
    14         print("用例2:这是一条注册测试用例")
    15 
    16     def tearDown(self) -> None:
    17         print("###这是每条测试用例的后置条件###")
    18 
    19     @classmethod
    20     def tearDownClass(cls) -> None:
    21         print("***这是该测试类中所有用例执行后的后置条件***")
    test_01demo

    14. unittest加载用例

          1> 通知测试类加载用例 loadTestsFromTestCase():通过一个一个的用例类名,将用例加载到测试套件中;

     1 import unittest
     2 from python基本语法.day15_unittest.test_01demo import TestDemo
     3 from python基本语法.day15_unittest.test_02demo import TestDemo2
     4 
     5 # 创建一个测试套件
     6 suite = unittest.TestSuite()
     7 # 创建一个用例加载器
     8 loader = unittest.TestLoader()
     9 # 将用例加载到测试套件
    10 # loader加载用例的方法如下:
    11 # 方式1:通过用例类名(loadTestsFromTestCase())
    12 suite.addTest(loader.loadTestsFromTestCase(TestDemo))
    13 suite.addTest(loader.loadTestsFromTestCase(TestDemo2))
    用例类加载用例

      缺点:用例类要一个一个的导入,之后再一个一个的加载到测试套件中,非常麻烦

          2> 通过用例模块加载用例 loadTestsFromModule():通过一个一个的用例模块名,将用例加载到测试套件中;

    1 from python基本语法.day15_unittest import test_01demo,test_02demo
    2 # 创建一个测试套件
    3 suite = unittest.TestSuite()
    4 # 创建一个用例加载器
    5 loader = unittest.TestLoader()
    6 # 方式2:通过用例模块名loadTestsFromModule()
    7 suite.addTest(loader.loadTestsFromModule(test_01demo))
    8 suite.addTest(loader.loadTestsFromModule(test_02demo))
    用例模块加载用例

         3> 通过用例文件所在路径加载用例 (loader.discover(路径,pattern='test*.py')):提供用例所在路径,结合匹配模式在该路径下查找满足条件的测试用例文件来加载用例;

              pattern指定匹配模式,如'test*.py'代表查找文件名以test开关,以.py结束的文件

     1 import unittest
     2 
     3 # 创建一个测试套件
     4 suite = unittest.TestSuite()
     5 # 创建一个用例加载器
     6 loader = unittest.TestLoader()
     7 # 方式3:通过用例文件所在路径(discover())
     8 suite.addTest(loader.discover(r'D:python_lemon37python基本语法day15_unittest',pattern='test*.py'))
     9 
    10 # 查看套件中的用例数量
    11 print(suite.countTestCases())
    用例路径加载用例

    15. unittest用例运行

          1> 执行用例时,不能直接使用unittest.TestRunner,因为它不是一个对外开放的API

    # 用例运行
    runer = unittest.TestRunner()
    runer.run(suite)  #报错,因为TestRunner类不是一个直接对外开放的API,不能直接使用

          2> 执行用例时,是使用 unittest.TextTestRunner() 类

               a> stream:用例执行结果输出到文件中

               b> verbosity:默认为1,还可以为0和2,为2时详情输出了用例执行结果是否成功,为0时输出的信息最少;

    # 用例运行
    with open('result.txt','a',encoding='utf-8') as fs:
        runer = unittest.TextTestRunner(stream=fs,verbosity=2)
        runer.run(suite)

           3> 前面加载用例时,要需经过三步(1. 创建测试套件 2. 创建用例加载器 3. 将用例加载到测试套件中 ),其实unittest提供了一个更简单的加载用例方法

    import unittest
    
    # 一步完成用例加载
    suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittest',pattern='test*.py')
    
    # 查看套件中的用例数量
    print(suite.countTestCases())
    
    # 用例运行
    with open('result.txt','a',encoding='utf-8') as fs:
        runer = unittest.TextTestRunner(stream=fs,verbosity=2)
        runer.run(suite)

           4> 生成 BeautifulReport 测试报告

                a> 下载安装包BeautifulReport-master.zip,解压后将文件名修改为BeautifulReport 

                b> 将整个包放到本地python的/Lib/site-packages目录下

                c> 运行用例,生成BeautifulReport报告

    # 一步完成用例加载
    from BeautifulReport import BeautifulReport
    suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittest',pattern='test*.py')
    # 运行用例生成BeautifulReport
    runer = BeautifulReport(suites=suite)
    runer.report(description='测试用例演示')

          5> 生成 HtmlTestRunner 报告

               a> 安装HtmlTestRunner:pip install html-testRunner

               b> 生成报告: runer = HTMLTestRunner()

               c> 生成一个文件夹reports,里面是按用例模块生成html报告的;

    from HtmlTestRunner import HTMLTestRunner
    
    suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittest',pattern='test*.py')
    
    # 运用用例生成报告
    runer = HTMLTestRunner()
    runer.run(suite)

            6> 生成HTMLTestRunnerNew报告:

                  1> 安装HTMLTestRunnerNew:在python第三方库管理平台官网 https://pypi.org/project/pip/

                  2> 生成报告:runer = HTMLTestRunner(stream=fs)

    # 运行用例生成报告
    from HTMLTestRunnerNew import HtmlTestRunner
    with open('result.html','wb') as fs:
        runer = HTMLTestRunner(stream=fs)
        runer.run(suite)

    16. unittest源码初认 leetcode

          1>  python中文说明文档:https://docs.python.org/zh-cn/3/library/index.html

           2> unittest源码说明文档:https://docs.python.org/zh-cn/3/library/unittest.html 

    17. unittestreport:核心的类 TestRunner 和 TestResult

          1> 安装:pip install unittestreport

           2> 特性:

                a> HTML测试报告;

                b> 测试用例失败重运行;

                c> 发送测试结果及报告到邮箱

                d> unittest数据驱动;

                e> 测试结果钉钉通知;

                f> 多线程执行用例;

           3> 核心源码查看地址: https://github.com/musen123/UnitTestReport/tree/master/unittestreport/core

    18. unittestreport生成报告:默认报告名为 report.html,可以传参修改报告相关的字段  filename   report_dir  title  tester  desc  templates

           1> 参数中的 templates 控制测试报告生成的样式,可以为1,2,3

    import unittest
    from unittestreport import TestRunner
    
    # 第1步:收集用例
    suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittest',pattern='test*.py')
    
    # 第2步:运行用例生成报告
    runer = TestRunner(suite,
                       filename="auto_report.html",
                       report_dir=".",
                       title='测试演示',
                       tester='天天',
                       desc="理赔项目测试报告",
                       templates=1)
    runer.run()

    19. unittestreport数据驱动 DataDrivenTest:将用例数据和用例逻辑代码进行分离,提高代码的重用率,以及提升代码的可维护性;

          1> pytest的数据驱动:用例数据参数化处理(即用例数据和用例逻辑代码是分离的),通过用例数据生成测试用例;

          2> 列表数据驱动:测试数据保存在列表中,通过@ddt装饰测试用例类,@list_data()装饰测试用例方法,其中case参数接收每一条用例数据;  

                a> 简单的测试数据:

    import unittest
    from unittestreport.core.dataDriver import ddt,list_data,yaml_data,json_data
    
    test_data = [(11,11),(22,22),(33,33),(44,44)]
    
    @ddt
    class TestDemo(unittest.TestCase):
    
        @list_data(test_data)
        def test_demo(self,case):
            self.assertEqual(case[0],case[1])

              b> list测试数据包含title字段:

    import unittest
    from unittestreport.core.dataDriver import ddt,list_data
    
    # 列表数据驱动-title字段为测试报告中每条测试用例的用例描述
    test_data = [{'data':('aa','aa'),'title':'用户登录成功'},
                 {'data':('',22),'title':'账号不能为空'},
                 {'data':(11,''),'title':'密码不能为空'},
                 {'data':(11,22),'title':'账号名不规范'}]
    @ddt
    class TestDemo(unittest.TestCase):
    
        @list_data(test_data)
        def test_demo(self,case):
            print(case['data'][0])
            self.assertEqual(case['data'][0],case['data'][1])

          3> yaml数据驱动:

              a> yaml格式的测试数据如下:

            b> 代码如下:

    import unittest
    from unittestreport.core.dataDriver import ddt,yaml_data
    
    @ddt
    class TestDemoYaml(unittest.TestCase):
    
        @yaml_data(r'case_data.yaml')
        def test_yaml(self,case):
            self.assertEqual(case['data']['aa'],case['data']['bb'])

            c> 执行结果多出一些打印内容:

             d> yaml文件每条数据的title字段为测试报告中的用例描述

          4> json测试数据:

                1> json格式的数据如下:

             2> 代码如下:

    import unittest
    from unittestreport.core.dataDriver import ddt,json_data
    
    # json数据驱动 json文件每条数据的title字段为测试报告中每条测试用例的用例描述
    @ddt
    class TestDemoJson(unittest.TestCase):
    
        @json_data(r'case_data.json')
        def test_json(self,case):
            self.assertEqual(case['data']['aa'],case['data']['bb'])

           3> json 文件每条数据的title字段为测试报告中的用例描述

     20. unittestreport重运行机制:

           1> @rerun装饰在测试用例方法上用于某个测试方法运行失败时重新运行;

                  a> 导入unittestreport中的rerun方法,再装饰在需要重运行的测试用例方法上即可   test_demo.py

    import unittest
    from unittestreport import rerun
    
    class TestDemo(unittest.TestCase):
    
        def test_01(self):  # 每条测试用例下面通过"""备注说明的内容,为测试报告中的用例描述
            """用户登录成功"""
            self.assertEqual(11,11)
    
        @rerun(3, 1)
        def test_02(self):
            """账号不能为空"""
            self.assertEqual('',22)
    
        def test_03(self):
            """密码错误"""
            self.assertEqual(33,34)
    
        def test_04(self):
            """账号错误"""
            self.assertEqual(44,45)

               b> 重运行匹配数据驱动ddt使用时,@rerun() 要在测试数据之后装饰  test_demo.py

    import unittest
    from unittestreport import rerun
    from unittestreport.core.dataDriver import ddt,list_data,yaml_data,json_data
    
    # yaml数据驱动 yaml文件每条数据的title字段为测试报告中每条测试用例的用例描述
    @ddt
    class TestDemoYaml(unittest.TestCase):
    
        @yaml_data(r'case_data.yaml')
        @rerun(3, 2)  # @rerun要放在@yaml_data之后装饰
        def test_yaml(self,case):
            self.assertEqual(case['data']['aa'],case['data']['bb'])
    
    
    # json数据驱动 json文件每条数据的title字段为测试报告中每条测试用例的用例描述
    @ddt
    class TestDemoJson(unittest.TestCase):
    
        @json_data(r'case_data.json')
        def test_json(self,case):
            self.assertEqual(case['data']['aa'],case['data']['bb'])

                c> 运行结果中 test_yaml测试用例方法 运行失败的用例都会重运行,用例会执行3次,每次间隔为2S,test_json 测试用例方法,运行失败的用例不会重运行;

        2>  运行用例调用 rerun_run() 方法,可以让所有测试用例中运行失败的用例重运行 run.py;

    import unittest
    from unittestreport import TestRunner
    
    # 一步完成用例加载
    suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittestunittestreport_demo',
                                                pattern='test*.py')
    
    # 生成unittestreport报告
    runer = TestRunner(suite,
                       filename="unit_report.html",
                       report_dir=".",
                       title='测试演示',
                       tester='天天',
                       desc="理赔项目测试报告",
                       templates=1)
    # runer.run()
    
    runer.rerun_run(2,1)  # 所以测试用例运行失败时,都会重运行

    PS:对单个用例设置重运行机制:在用例方法上面直接使用@rerun()来指定重运行的次数和运行的时间间隔;

            对所有用例设置重运行机制:直接使用rerun_run()方法来运行用例;

    21. unittestreport 多线程运行用例:考虑用例在执行时,上下用例可能存在前后依赖关系,所以该多线程的设计是基于用例测试类之间的并发(不是每个测试用例的多并发),所以用例测试类之前不能存在先后依赖关系,否则多线程执行用例时,就会导致用例执行失败;

         1> 测试用例代码: test_thread.py,单线程执行时最少需要30S

     1 import time
     2 import unittest
     3 from unittestreport.core.dataDriver import ddt,list_data
     4 
     5 
     6 @ddt
     7 class TestThread(unittest.TestCase):
     8 
     9     @list_data(range(10))
    10     def test_demo(self,case):
    11         time.sleep(1)
    12 
    13     @list_data(range(10))
    14     def test_login(self, case):
    15         time.sleep(1)
    16 
    17     @list_data(range(10))
    18     def test_register(self, case):
    19         time.sleep(1)
    test_thread.py

         2> 运行用例脚本:run.py,3个线程执行时,时间大概为10S多

     1 import unittest
     2 from unittestreport import TestRunner
     3 
     4 # 一步完成用例加载
     5 suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittestunittestreport_thread',
     6                                             pattern='test*.py')
     7 
     8 # 生成unittestreport报告
     9 runer = TestRunner(suite,
    10                    filename="thread_report.html",
    11                    report_dir=".",
    12                    title='测试演示多线程执行用例',
    13                    tester='小夕',
    14                    desc="理赔项目测试报告",
    15                    templates=1)
    16 
    17 # 多线程运行用例
    18 runer.run(thread_count=3)
    run.py

         3> 多线程注意事项:

             a> 确保每个用例测试类没有先后依赖关系;

             b> 确保每个线程任务(用例测试类)在执行的时候不会出现资料竞争(对全局依赖的数据进行修改);

    PS:各个用例测试类之间使用多线程并发执行以提高测试用例执行效率,但是每个用例测试类中的用例仍是让其顺序执行的,以保证用例测试类中的用例先后依赖正常执行;

    22. unittestreport测试结果发送邮件

         1> 开启qq邮件的SMTP服务,获取到授权码

         2> 发送邮件代码如下:run.py

    import unittest
    from unittestreport import TestRunner
    
    # 一步完成用例加载
    suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittestunittestreport_thread',
                                                pattern='test*.py')
    
    # 生成unittestreport报告
    runer = TestRunner(suite,
                       filename="thread_report.html",
                       report_dir=".",
                       title='测试演示多线程执行用例',
                       tester='小夕',
                       desc="理赔项目测试报告",
                       templates=1)
    
    # 多线程运行用例
    runer.run(thread_count=3)
    
    # 发送邮件  host, port, user, password-授权码, to_addrs
    runer.send_email(host='smtp.qq.com',
                     port='465',
                     user='503587478@qq.com',
                     password='izxxoylgjlkqbiih',
                     to_addrs=['503587478@qq.com','240911074@qq.com'])

        3> 邮件收到内容如下:

    23. unittestreport测试结果发送到钉钉群

          1> 在钉钉自定义一个机器人,获取到该机器人的webhook地址

           2> 调用发送消息给钉钉的方法 runer.dingtalk_notice(url,key='自动化')

               a> url:webhook地址;

               b> key:自定义机器人时,安全设置中的自定义关键词,如为 自动化;

               c> secret:自定义机器人时,安全设置中的加签;

               d> atMobiles:发送通知钉钉中要@人的手机号列表;

    import unittest
    from unittestreport import TestRunner
    
    # 一步完成用例加载
    suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittestunittestreport_demo',
                                                pattern='test*.py')
    
    # 生成unittestreport报告
    runer = TestRunner(suite,
                       filename="unit_report.html",
                       report_dir=".",
                       title='测试演示',
                       tester='天天',
                       desc="理赔项目测试报告",
                       templates=1)
    runer.run()
    
    # 测试结果发送钉钉群 -- 一家人
    # 1. 给钉钉群自定义机器人时,安全设置的自定义关键词(key), 加签(SEC668f9f9711694d052c95cd4320ce6e9ab5bb5e0de1c42fe4ca2a371da5afa7a1)
    secret = 'SEC668f9f9711694d052c95cd4320ce6e9ab5bb5e0de1c42fe4ca2a371da5afa7a1'
    # 2. 给钉钉群自定义机器人后得到的webhook地址
    url='https://oapi.dingtalk.com/robot/send?access_token=7fe2f381892d5c3938bbdcca138342afce05cb0a8ba29cf7e222824104381c7d'
    # 3. atMobiles: 发送通知钉钉中要@人的手机号列表
    # 4. 推荐消息给钉钉
    res = runer.dingtalk_notice(url,key='自动化',secret=secret)
    print(res)  # 返回是否发送成功

    24. unittestreport测试结果发送到企业微信群

           1>  调用发送消息给企业微信的方法 runer.weixin_notice()

                a> chatid: 企业微信群ID

                b> access_token: 调用企业微信API接口的凭证

                c> corpid: 企业ID

                e> corpsecret:应用的凭证密钥

           2> 以上相关参数的值获取可以查看连接: https://work.weixin.qq.com/api/doc/90001/90143/90372

           3> 测试结果推送到企业微信群,【access_token】和【corpid,corpsecret】至少要传一种

    import unittest
    from unittestreport import TestRunner
    
    # 一步完成用例加载
    suite = unittest.defaultTestLoader.discover(r'D:python_lemon37python基本语法day15_unittestunittestreport_demo',
                                                pattern='test*.py')
    
    # 生成unittestreport报告
    runer = TestRunner(suite,
                       filename="unit_report.html",
                       report_dir=".",
                       title='测试演示',
                       tester='天天',
                       desc="理赔项目测试报告",
                       templates=1)
    runer.run()
    
    # 测试结果发送企业微信
    runer.weixin_notice(chatid='',
                        access_token=None,
                        corpid=None,
                        corpsecret=None)

     24. python的RSA加解密

         1> pycrypto,pycrytodome和crypto是一个东西,crypto在python上面的名字是pycrypto它是一个第三方库,但是已经停止更新三年了,所以不建议安装这个库;

         2> windows下python3.6安装也不会成功!这个时候pycryptodome就来了,它是pycrypto的延伸版本,用法和pycrypto 是一模一样的;

        3> 安装pycrytodome: pip install pycryptodome

        4> 在使用的时候导包是有问题的,会报错:

         解决方案:这个时候只要修改一个文件夹的名称就可以完美解决这个问题

          python的安装路径下:Python36Libsite-packages

         下面有一个文件夹叫做crypto,将c改成C,对就是改成大写就ok了!!!

      25. RSA公私钥加解密: 使用公钥来加密信息,然后使用私钥来解密

         1> 基于已有的公钥(字符串) 和 私钥(字符串) 进行RSA加密:

    import base64
    import json
    
    from Crypto.PublicKey import RSA
    from Crypto.Cipher import PKCS1_v1_5
    
    #lxPubKey  公钥
    lxPubKey="MIGfMAfegwggwogewgzCqynB+h7GNADCBiQKBgQDczg7E4gLH5DCBILt5Ooz5G8bRxBL2idtvwkFNZ3wdY2/l9zWUI5yD/x+fH8YF0LkO5S7QCxHcrdIKPrKnJE3I5cz1HykP31etvQ6gD6ohBOy67mfiUDjLFx8e68S+FJvpJdfeogwggew"
    
    #lxPriKey 私钥
    lxPriKey="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAEGWGegosgiwgwggu3k5HEEvaJ22/CQU1nfB1jb+X3NZQjnIP/H58fxgXQuQ7lqcfKQ/fV629DqAPqjPkbxuiEE7LruZ+JQOMsXcrzFkKa3uTtBOa6Xk2R61zBkucEGWGWsgwoegogwbwxTQJBAP0wGenVOq4dCPdhyghkKT6SL2w/SgbrROiJ1mG9MoBq58/0gk85I91R7nuvOYTKTUkhWdPYITpDarmPJzesCQGRBmOMgHCHH0NfHV3Gn5rz+61ebIoyD4uar4P7RrRvQg5mFZFDafVHapceh4CsWCiyAfzhj9229sTecvdbr68Lb0zphO3I5EMfqPbTSq2oZq8thvGlyFyI7SNNuvAHjRFoPP6WoVoxp5LhWHT2l/DqqWakImXA0eZYNny0vETQJAVHsQUZaLOzexEKcujo1/YH6A0dSEbTkxHr3aJh8IVh3SzT5ejdYutVrdi1c8+R9Zg=="
    
    data_dict={"creditApplyNo": "O201806108205659"}
    data_str=json.dumps(data_dict)
    
    print(data_str)
    
    def rsa_encrpyt(encryptstr, publickey):
        encryptstr = bytes(encryptstr, encoding='utf-8')
        publickey = base64.b64decode(publickey)
       
        rsakey = RSA.importKey(publickey)
        cipher = PKCS1_v1_5.new(rsakey)
        pkcs1_padding_text = cipher.encrypt(encryptstr)
        cipher_text = base64.b64encode(pkcs1_padding_text)
        return str(cipher_text, encoding='utf-8')
    
    print(rsa_encrpyt(data_str,lxPubKey))

        2> 基于标准的 .pem文件进行rsa加解密:

    import rsa
    import base64
    
    # 1.先生成一对密钥,然后保存.pem格式文件
    (pubkey, privkey) = rsa.newkeys(1024)
    
    pub = pubkey.save_pkcs1()
    pubfile = open('public.pem','wb+')
    pubfile.write(pub)
    pubfile.close()
    
    pri = privkey.save_pkcs1()
    prifile = open('private.pem','wb+')
    prifile.write(pri)
    prifile.close()
    
    # 2.load  rsa公钥和rsa密钥
    message = 'lovesoo.org'
    print("原信息:",message)
    with open('public.pem','rb') as publickfile:
        p = publickfile.read()
        pubkey = rsa.PublicKey.load_pkcs1(p)
    
    with open('private.pem','rb') as privatefile:
        p = privatefile.read()
        privkey = rsa.PrivateKey.load_pkcs1(p)
    
    # 3.用公钥加密、再用私钥解密
    message_byte = message.encode('utf-8')
    crypto_byte = rsa.encrypt(message_byte, pubkey)  #加密出来的字节
    cryp_msg = base64.b64encode(crypto_byte).decode("utf-8")  # base64编码
    print("加密后的信息:",cryp_msg)
    decry_msg = rsa.decrypt(crypto_byte, privkey)
    message_str = decry_msg.decode('utf-8')
    print("解密后的信息:",message_str)
    
    # 4.sign 用私钥签名认证、再用公钥验证签名
    signature = rsa.sign(decry_msg, privkey, 'SHA-1')
    rsa.verify(b'lovesoo.org', signature, pubkey)
    .pem文件rsa加解密

        运行脚本后,生成的.pem文件如下:

  • 相关阅读:
    HTML元素解释
    Java命名规范
    HDU 1058 Humble Numbers(DP,数)
    HDU 2845 Beans(DP,最大不连续和)
    HDU 2830 Matrix Swapping II (DP,最大全1矩阵)
    HDU 2870 Largest Submatrix(DP)
    HDU 1421 搬寝室(DP)
    HDU 2844 Coins (组合背包)
    HDU 2577 How to Type(模拟)
    HDU 2159 FATE(二维完全背包)
  • 原文地址:https://www.cnblogs.com/quiet-sun/p/14366054.html
Copyright © 2011-2022 走看看