• Unittest高级应用


    1.TestSuite

    TestSuite类用来对测试用例进行组合分类,通常称为测试套件。我们可以将不同位置的测试用例集合到一个测试套件内,并利用其内部实现的run方法来执行。

    为了直观的理解TestSuite的用法,下面先定义了两个测试类。每个测试类里分别定义了几个方法。

    import unittest

    class TestDemo01(unittest.TestCase):

        def test01(self):

            print('This is test01.')

        def test02(self):

            print('This is test02.')

        def test03(self):

            print('This is test03.')

    class TestDemo02(unittest.TestCase):

        def test04(self):

            print('This is test04.')

        def test05(self):

            print('This is test05.')

    在执行的代码段中,首先加入TestDemo01中的test02方法来构建出测试套件suite01,然后重新构建一个测试套件suite02,包含suite01与TestDemo02中的test04方法共同构成的一个元组。最后使用run方法将执行的结果保存到r变量中。这里需要特别说明,tests的参数类型必须是可迭代的,例如本例中的列表和元组。

    if __name__ == '__main__':

        suite01 = unittest.TestSuite(tests=[TestDemo01('test02')])

        suite02 = unittest.TestSuite(tests=(suite01,TestDemo02('test04')))

        r = unittest.TestResult()

        suite02.run(result=r)

        print(r.__dict__)

    本例中调用的是suite02中的run方法,那么运行的是suite02套件内的测试用例,依次为test02和test04。

    This is test02.

    This is test04.

    {'failfast': False, 'failures': [], 'errors': [], 'testsRun': 2, 'skipped': [], 'expectedFailures': [], 'unexpectedSuccesses': [], 'shouldStop': False, 'buffer': False, 'tb_locals': False, '_stdout_buffer': None, '_stderr_buffer': None, '_original_stdout': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>, '_original_stderr': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, '_mirrorOutput': False, '_testRunEntered': False, '_moduleSetUpFailed': False, '_previousTestClass': <class '__main__.TestDemo02'>}

    TestSuite类中除了run()以外,还有一些重要的方法:addTest(test)和addTests(tests)。addTest(test)为添加测试用例,参数可以是TestCase或TestSuite的实例。addTests(tests)顾名思义,是对TestCase或TestSuite的实例的多个迭代进行添加,其内部实现原理依然是调用addTest(test)。简单点说,前者是添加单个测试用例,而后者是为了一次性添加多个测试用例。

    依然沿用TestDemo01和TestDemo02的例子,执行代码做了一些调整,首先得到测试套件实例suite,分别调用addTest和addTests方法来添加测试用例,注意在addTests中只有一个参数,并且类型是元组,最后执行并输出测试结果的字典。这里也额外使用了另外一个TestSuite中的常用方法countTestCases(),作用是返回执行的测试方法的个数。

    if __name__ == '__main__':

        suite = unittest.TestSuite()

        suite.addTest(TestDemo01('test02'))

        suite.addTests((TestDemo02('test04'),TestDemo02('test05')))

        r = unittest.TestResult()

        suite.run(result=r)

    print(r.__dict__)

    print(suite.countTestCases())

    执行结果不再赘述,仍然是依次执行添加的各个测试用例,然后输出总共执行的测试方法个数3。

    This is test02.

    This is test04.

    This is test05.

    {'failfast': False, 'failures': [], 'errors': [], 'testsRun': 3, 'skipped': [], 'expectedFailures': [], 'unexpectedSuccesses': [], 'shouldStop': False, 'buffer': False, 'tb_locals': False, '_stdout_buffer': None, '_stderr_buffer': None, '_original_stdout': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>, '_original_stderr': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, '_mirrorOutput': False, '_testRunEntered': False, '_moduleSetUpFailed': False, '_previousTestClass': <class '__main__.TestDemo02'>}

    3

    2.TestLoader

    TestLoader是Unittest框架中的一个重要的类,可以从被测试的类和模块中创建测试套件,即TestSuite。通常我们不用对他进行实例化,使用匿名对象的方式来使用即可。TestLoader中提供了如下一些常用的方法。

    (1)loadTestsFromTestCase(testCaseClass) :从某个类中加载所有的测试方法,参数为加载的类名,testCaseClass必须继承于TestCase。

    (2)loadTestsFromModule(module, pattern=None) :从某个模块中加载所有的测试方法,参数为模块名,即文件名。当该模块中有多个类都继承于TestCase时,那么这些类里的测试方法均会被执行。

    (3)loadTestsFromName(name, module=None) :加载某个单独的测试方法,参数name是一个string,格式为“module.class.method”。

    (4)loadTestsFromNames(name, module=None) :names是一个list,用法与上面相同。

    下面依然沿用前面计算器的例子来进行讲解。模块名为Test01.py,代码中被测试类为Calculator,TestDemo01与TestDemo02均为测试类,并继承于unittest.TestCase,每个测试类中各有一个测试方法。在执行的代码中,分别使用类名、模块名、方法的方式来加载要测试的方法,并添加到了测试集suite中,最后执行测试集。

    import unittest

    # 导入当前的Test01模块,用于后续使用

    from C03_Unittest.Ex03_TestLoader import Test01

    class Calculator:

        def divide(self,x,y):

            return x / y

    class TestDemo01 (unittest.TestCase):

        def test01(self):

            print('This is test01.')

            cal = Calculator()

            result = cal.divide(10,2)

            self.assertEqual(result,5)

    class TestDemo02(unittest.TestCase):

        def test02(self):

            print('This is test02.')

            cal = Calculator()

            result = cal.divide(0,2)

            self.assertEqual(result,0)

    if __name__ == '__main__':

        suite = unittest.TestSuite()

        # 加载该类

        testCase01 = unittest.TestLoader().loadTestsFromTestCase(TestDemo01)

        # 加载整个模块

        testCase02 = unittest.TestLoader().loadTestsFromModule(Test01)

        # 加载TestDemo01类中的测试方法test01

        testCase03 = unittest.TestLoader().loadTestsFromName('C03_Unittest.Ex03_TestLoader.Test01.TestDemo01.test01')

        suite.addTests(testCase01)

        suite.addTests(testCase02)

        suite.addTests(testCase03)

        r = unittest.TestResult()

        suite.run(result=r)

        print(r.__dict__)

    运行结果如下,未出现失败和错误。仔细分析可以看到,由于先执行suite.addTests(testCase01), TestDemo01类中的方法被运行了,输出了“This is test01.”。接下来执行suite.addTests(testCase02),整个模块即Test01.py里的所有测试方法被执行,所以分别输出了“This is test01.”和“This is test02.”。最后加载的是testCase03,而他仅仅只是TestDemo01中的方法test01,所以又输出了一次“This is test01.”。

    This is test01.

    This is test01.

    This is test02.

    This is test01.

    {'failfast': False, 'failures': [], 'errors': [], 'testsRun': 4, 'skipped': [], 'expectedFailures': [], 'unexpectedSuccesses': [], 'shouldStop': False, 'buffer': False, 'tb_locals': False, '_stdout_buffer': None, '_stderr_buffer': None, '_original_stdout': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>, '_original_stderr': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, '_mirrorOutput': False, '_testRunEntered': False, '_moduleSetUpFailed': False, '_previousTestClass': <class 'C03_Unittest.Ex03_TestLoader.Test01.TestDemo01'>}

    其实在TestLoader中,还有其他的方法,比如discover,可以找到某个目录下的所有测试模块下的测试方法,这里就不详细介绍,请大家自行查看官网文档掌握其用法。

    3.装饰器

    在unittest中提供了装饰器的功能,比如我们想跳过某些方法的执行,或者直接设置某些方法为预期失败的,这时就会用到装饰器。对需要处理的方法的前一行加上诸如“@XX”,则可以实现相应的功能,常用的装饰器有以下几种。

    (1)@unittest.skip(reason):无条件跳过测试,reason描述为什么跳过测试。

    (2)@unittest.skipif(conditition,reason):condititon为条件,当条件为true时则跳过测试。

    (3)@unittest.skipunless(condition,reason):condition为条件,与上面相反,当条件不是true时则跳过测试。

    (4)@unittest.expectedFailure():标记该测试预期为失败 ,如果该测试方法运行失败,则该测试不算做失败。

    沿用前面的例子,分别对4个方法设置不同的装饰器。

    import unittest

    import sys

    class Calculator:

        def divide(self,x,y):

            return x / y

    class TestDemo (unittest.TestCase):

        def setUp(self):

            self.a = 10

            self.b = 20

        @unittest.skip('强制跳过')

        def test01(self):

            print('This is test01.')

            cal = Calculator()

            result = cal.divide(10,2)

            self.assertEqual(result,3)

        @unittest.skipIf( 10 > 5, "满足条件则跳过")

        def test02(self):

            print('This is test02.')

            cal = Calculator()

            result = cal.divide(10,2)

            self.assertEqual(result,3)

        @unittest.skipUnless( 10 > 5, "不满足条件则跳过")

        def test03(self):

            print('This is test03.')

            cal = Calculator()

            result = cal.divide(10,2)

            self.assertEqual(result,3)

        @unittest.expectedFailure

        def test04(self):

            print('This is test04.')

            cal = Calculator()

            result = cal.divide(10,2)

            self.assertEqual(result,3)

    if __name__ == '__main__':

        suite = unittest.TestSuite()

        # 加载整个类中的测试方法

        testCase = unittest.TestLoader().loadTestsFromTestCase(TestDemo)

        suite.addTests(testCase)

        r = unittest.TestResult()

        suite.run(result=r)

        print(r.__dict__)

    运行结果如下。四个方法都分别设置了装饰器,可以看到被跳过的是前两个,第三个方法test03由于条件不满足,正常执行,第四个方法也执行了,但即使失败也没有计入到failures中。

    This is test03.

    This is test04.

    {'failfast': False, 'failures': [(<__main__.TestDemo testMethod=test03>, 'Traceback (most recent call last):  File "C:/Users/Administrator/PycharmProjects/python364/C03_Unittest/Ex05_Skip/Test01.py", line 32, in test03    self.assertEqual(result,3) AssertionError: 5.0 != 3 ')], 'errors': [], 'testsRun': 4, 'skipped': [(<__main__.TestDemo testMethod=test01>, '强制跳过'), (<__main__.TestDemo testMethod=test02>, '满足条件则跳过')], 'expectedFailures': [(<__main__.TestDemo testMethod=test04>, 'Traceback (most recent call last):  File "C:/Users/Administrator/PycharmProjects/python364/C03_Unittest/Ex05_Skip/Test01.py", line 39, in test04    self.assertEqual(result,3) AssertionError: 5.0 != 3 ')], 'unexpectedSuccesses': [], 'shouldStop': False, 'buffer': False, 'tb_locals': False, '_stdout_buffer': None, '_stderr_buffer': None, '_original_stdout': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>, '_original_stderr': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, '_mirrorOutput': False, '_testRunEntered': False, '_moduleSetUpFailed': False, '_previousTestClass': <class '__main__.TestDemo'>}

    4.TestResult

    顾名思义,TestResult类是为了保存测试结果而专门设计的。从前面的例子可知,执行测试时最终都需要调用run函数,而run函数则必须传入一个参数result,这个result就是TestResult对象或者是其子类的对象。下面是run方法的源码实现,代码较长,只贴出一部分,目录是让大家明确result是run方法的必须参数。

        def run(self, result, debug=False):

            topLevel = False

            if getattr(result, '_testRunEntered', False) is False:

                result._testRunEntered = topLevel = True

            ...

            ...

                 return result

    我们单独贴出核心的代码段让大家理解,r为TestResult的实例,无论是以何种方式调用run方法,都需要将TestResult实例化的对象作为参数传递给run。

    if __name__ == '__main__':

        suite = unittest.TestSuite()

        suite.addTests((TestDemo02('test04'),TestDemo02('test05')))

        r = unittest.TestResult()

        suite.run(result=r)

        print(r.__dict__)

    TestResult的内容非常丰富,对测试结果做了详细的分类,下面的运行结果大家一定不会陌生,在前面的若干个例子里都出现过类似的部分。

    {'failfast': False, 'failures': [], 'errors': [], 'testsRun': 3, 'skipped': [], 'expectedFailures': [], 'unexpectedSuccesses': [], 'shouldStop': False, 'buffer': False, 'tb_locals': False, '_stdout_buffer': None, '_stderr_buffer': None, '_original_stdout': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>, '_original_stderr': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, '_mirrorOutput': False, '_testRunEntered': False,'_moduleSetUpFailed': False, '_previousTestClass': <class '__main__.TestDemo02'>}

    3

    TestResult的属性几乎涵盖了我们需要对当前测试了解的所有信息,下面抽取其中最重要的属性来进行解释。

    (1)failfast:值为True或False,当设置为True时,测试过程中遇到失败或者错误,则立即终止后续的测试,通常我们保持False即可。

    (2)failures:失败,这里会列出使用断言方法失败的情况。

    (3)errors:错误,这里会列出程序出现的异常错误。

    (4)testsRun:已经运行的所有测试的数量。

    (5)skipped:列出跳过的测试方法及原因。

    (6)expectedFailures:列出预期失败的方法。

    (7)unexpectedSuccesses:列出标记为预期失败,但实际运行却又成功的方法。

  • 相关阅读:
    SAP ABAP 开发中常用的方法总结
    安装sap帮助文档
    spring成神之路第七篇:DI依赖注入之手动注入
    spring成神之路第三篇:Spring容器基本使用及原理(ApplicationContext接口获取bean的4种方式)
    spring成神之路第九篇:dependon到底是干什么的?
    spring成神之路第十篇:primary可以解决什么问题?
    spring成神之路第四篇:xml中bean定义详解
    spring成神之路第六篇:玩转bean scope,避免跳坑里
    spring成神之路第一篇:为何要学spring
    spring成神之路第五篇:创建bean实例的方式
  • 原文地址:https://www.cnblogs.com/woniuxy/p/10955720.html
Copyright © 2020-2023  润新知