• Python3+unitest自动化测试初探(下篇)



    unittest官方文档
    本篇随笔承接:
    Python3+unitest自动化测试初探(中篇)
    Python3+unitest自动化测试初探(上篇)

    9、用例结果校验

    用unittest来完成自动化测试时,结果校验无疑是非常重要的。用例校验不完整或者校验过于简单,是无法发现产品的缺陷或Bug的。就自动化测试来而言,其目的是代替部分手动测试,将测试人员从繁重重复的功能测试中解放出来。试想一下,一些缺少校验或简单校验的用例运行完后通过率即使达到100%,这能说明产品或版本没有任何问题吗?能够增强版本发布的信心吗?unittest提供了很多断言方法用于测试结果的校验。常用的断言方法如下:


    所有的断言方法都提供一个可选参数msg,用于在测试失败时显示。

    • assertEqual(a, b,msg=None):校验a是否等于b,如果相等,测试通过,反之则测试失败。msg为可选参数,如果传入了该参数,在测试失败时会打印。
    >>> class assertTest(unittest.TestCase):
    ... 	pass
    ... 
    >>> ast1 = assertTest() 
    
    >>> ast1.assertEqual(1,2,msg="1 is not equal to 2")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/local/lib/python3.7/unittest/case.py", line 839, in assertEqual
        assertion_func(first, second, msg=msg)
      File "/usr/local/lib/python3.7/unittest/case.py", line 832, in _baseAssertEqual
        raise self.failureException(msg)
    AssertionError: 1 != 2 : 1 is not equal to 2
    
    • assertNotEqual(a, b, msg=None):校验a是否不等于b,如果不相等,测试通过。反之则测试失败。
    • assertTrue(x,msg=None):验证x是否为True,x可以为值或表达式,x为True则测试通过。反之,则测试失败,msg为可选参数,如果传入了该参数,在测试失败时会打印。
    >>> ast1.assertTrue(1=2,msg="1=2,False")
      File "<stdin>", line 1
    SyntaxError: keyword can't be an expression
    >>> ast1.assertTrue(1==2,msg="1=2,False")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/local/lib/python3.7/unittest/case.py", line 692, in assertTrue
        raise self.failureException(msg)
    AssertionError: False is not true : 1=2,False
    
    
    • assertIs(a, b):判断a is b是是否成立。等价于判断id(a) == id(b)是否成立,如果成立,则测试通过。反之,测试失败。
    • assertIsNone(x):判断x是否为None。如果为None则测试通过,反之,测试失败。
    • assertIn(a, b):判断a是否在b中。如果在,则测试通过,反之,测试失败。
    • assertIsInstance(a, b):判断a是否为b的实例。如果是,则测试通过,反之,测试失败。

    unittest还提供了如下方法用于检查异常,警告,日志信息。图片来自unittest官方文档。

    检查异常,警告,日志的使用方法大同小异。在此不一一说明了,下面举例说明assertRaises(exc, fun, *args, **kwds)和assertRaises(exc, fun, *args, **kwds)的用法。
    [ 示例1 ] assertRaises()的用法

    #coding:utf-8
    '''
    定义一个除法的函数
    当y==0时,会抛出错误ZeroDivisionError
    '''
    import unittest
    def div(x,y):
            return  x/y
    
    #定义一个测试类assertTest,继承于unittest.TestCase
    class assertTest(unittest.TestCase):
        #pass表示什么也不做
        pass
    ast2 = assertTest()
    '''
    第一个参数为异常的类型
    第二个为方法名称
    后面为方法的参数
    '''
    ast2.assertRaises(ZeroDivisionError,div,1,0)
    print("---------------------------------------")
    ast2.assertRaises(ZeroDivisionError,div,1,1)
    

    调用div(1,1)不会抛出异常ZeroDivisionError,所以assertRaises()测试失败,上述代码运行结果如下:

    如果只传入了expetion或msg参数,就可以在with上下文管理器中测试一段代码而不仅仅是测试函数,就像下面这样。

    with ast2.assertRaises(ZeroDivisionError):
         div(1,1)
    
    with ast2.assertRaises(ZeroDivisionError):
        div(1,0)
    
    

    运行结果如下:

    [ 示例2 ] assertRaises()的用法

    #导入warnings模块
    import warnings
    #定义一个简单的函数,打印级别为UserWarning的告警
    def warnTest():
         warnings.warn("This is warning test")
    
    ast2.assertWarns(UserWarning,warnTest)
    print("--------------------------------")
    with ast2.assertWarns(UnicodeWarning):
        warnTest()
    

    warnTest()函数不会打印UnicodeWarning级别的告警,所以第二个告警校验会失败,运行结果如下:


    点击这里返回本篇目录

    10、跳过用例

    在一些场景下并不会执行所有的用例,而是选择性跳过一部分用例。unittest支持跳过单个用例或整个测试类。跳过用例需要用到unittest.skip()装饰器。

    • @unittest.skip(reason):无条件跳过单个用例或测试类,reason是跳过的原因。
    • @unittest.skipIf(condition, reason):if条件成立则跳过单个用例或测试类,reason是跳过的原因。
    • @unittest.skipUnless(condition, reason):条件为False则跳过单个用例或测试类,reason是跳过的原因。
    • @unittest.expectedFailure:标记测试用例为失败,不会出现在统计结果中。
    • exception unittest.SkipTest(reason):跳过用例并抛出异常。

    [ 示例3 ]:直接跳过注册功能的用例
    对userRegTest.py做如下修改,并执行userRegTest.py:

        @unittest.skip("skip test case of user reg")
        def test_pwdlenerr_L1(self):
            print("test case:test_pwdlenerr_L1")
            res = self.user1.userReg()
            self.assertEqual(res,"passwordLenError")
    
        #测试场景:正常注册
        @unittest.skipIf(2>1,"skip if condiction")
        def test_regsucess_L0(self):
            print("test case:test_regsucess_L0")
            res = self.user2.userReg()
            self.assertEqual(res,"regSucess")
    
        #测试场景:用户名重名
        @unittest.skipUnless(1<0,"skip unless.")
        def test_regagain_L1(self):
            print("test case:test_regagain_L1")
            res = self.user3.userReg()
            self.assertEqual(res,"SameNameError")
    
    

    执行结果如下:

    11、Test Discovery

    unittest支持用例发现功能,在unittest.defaultTestLoader中实现。使用的时候需要传入两个参数:寻找的起始目录,匹配的测试文件的格式(默认test*.py)。用例发现也支持命令行模式。

    [ 示例4 ]:testDiscover

        startPath = './testCases'
        discover = unittest.defaultTestLoader.discover(start_dir=startPath,pattern='*Test.py')
        print(discover)
    

    运行结果:

    12、加载用例

    unittest支持从模块和测试类中提取测试用例创建测试套。在class unittest.TestLoader中实现。TestLoader常见的加载方法如下:

    • loadTestsFromTestCase(testCaseClass):从unittest.TestCase的子类即测试类中加载用例并返回测试套。
    • loadTestsFromName(name,module=None):从特定的字符串说明符中加载用例并返回测试套。
    • loadTestsFromNames(names,module=None):用法和 loadTestsFromName(name,module=None)类似,不同的是它可以接受字符串说明符列表,而不是一个。
    • loadTestsFromModule(module, pattern=None):从模块中加载所有测试用例,返回一个测试套件。
    • getTestCaseName(testCaseClass):返回一个有序的包含在testCaseClass中的方法名列表。

    [示例5 ] :TestLoader用法举例

    
    #coding:utf-8
    
    import unittest
    import testCases.userLoginTest,testCases.userRegTest
    from testCases.userRegTest import regTest
    from testCases.userLoginTest import loginTest
    
    loader = unittest.TestLoader()
    
    print("从测试类loginTest加载所有的用例:")
    caseInLoginTest = loader.getTestCaseNames(loginTest)
    print(caseInLoginTest)
    
    print("从测试模块中加载用例:")
    loadByModule = loader.loadTestsFromModule(testCases.userLoginTest)
    print(loadByModule)
    
    print("从测试类中加载用例:")
    loadByTestClass = loader.loadTestsFromTestCase(regTest)
    print(loadByTestClass)
    print("从特定的字符串标识符中加载用例")
    
    loadBySpecifier = loader.loadTestsFromName("regTest.test_regagain_L1")
    print(loadBySpecifier)
    
    print("从字符符标识符列表中加载用例")
    specStrs = ["regTest.test_regagain_L1","test_regsucess_L0"]
    loadBySpecifiers = loader.loadTestsFromNames(specStrs)
    print(loadBySpecifiers)
    
    

    运行结果如下:

    在发布时间紧张,产品迭代频繁的情况下,很多时候上线一个新特性,对已发布的老特性采用的回归策略是:只执行L0即(Level 0)级别的测试用例。这个在unittest里面怎么实现呢?解决方案是:

    1. 规范特性L0用例的命名格式,比如:test_xxx_L0.
    2. 加载用例
    3. 通过正则表达式匹配出L0级别的用例集合,然后交给test runner执行。

    (完)
    点击这里返回本篇目录

  • 相关阅读:
    Java 实现常见内排序
    markdown基本语法
    HashMap (JDK1.8) 分析
    jQuery总结
    JS 数组 常用方法
    CSS样式 解决文字过长显示省略号问题
    Python中的 __name__属性的含义和作用
    http协议详细介绍
    Http和Https的区别
    爬虫——requests库使用方法
  • 原文地址:https://www.cnblogs.com/webDepOfQWS/p/10732345.html
Copyright © 2020-2023  润新知