python unittest全面解析

import unittest
import os
class MyUnittest(unittest.TestCase):
    def setUp(self) -> None:
        print("setUp_每运行一次用例都会执行一次")
        self.name = "hi"
        print("setUp_name_%s" % self.name)
    def tearDown(self) -> None:
        print("tearDown_每结束一次用例都会执行一次")
        print("tearDown_name_%s" % self.name)
    @classmethod
    def setUpClass(cls) -> None:
        print("setUpClass_整个测试开始后执行,只执行一次")
        cls.func = "setUpClass"
        print("setUpClass_func_%s" % cls.func)
    @classmethod
    def tearDownClass(cls) -> None:
        print("tearDownClass_整个测试完成后执行,只执行一次")
        print("tearDownClass_func_%s" % cls.func)
    def test_add(self):
        print("测试函数:test_add")
        self.name = "test_add"
        print("setUpClass_cls.func_%s" % self.func)
    def test_subtract(self):
        print("测试函数:test_subtract")
        self.name = "test_subtract"
        self.func = "test_subtract"
if __name__ == "__main__":
    # 运行方式一
    unittest.main()
setUpClass_整个测试开始后执行,只执行一次
setUpClass_func_setUpClass
test_add (ut_module.ut1_test.MyUnittest) ... setUp_每运行一次用例都会执行一次
setUp_name_hi
测试函数:test_add
setUpClass_cls.func_setUpClass
tearDown_每结束一次用例都会执行一次
tearDown_name_test_add
test_subtract (ut_module.ut1_test.MyUnittest) ... setUp_每运行一次用例都会执行一次
setUp_name_hi
测试函数:test_subtract
tearDown_每结束一次用例都会执行一次
tearDown_name_test_subtract
tearDownClass_整个测试完成后执行,只执行一次
tearDownClass_func_setUpClass
----------------------------------------------------------------------
Ran 2 tests in 0.003s

setUp初始化的值可以改变

setUp_name_hi
测试函数:test_add
setUpClass_cls.func_setUpClass
tearDown_每结束一次用例都会执行一次
tearDown_name_test_add
setUp中的self.name的值,在测试函数中是可以被改变的,一开始name为hi,后面为test_add

setUpClass的的值无法改变

在def setUpClass赋值
self.func = "setUpClass"
在 def test_subtract中赋值
self.func = "test_subtract"
# 执行结果
setUpClass_整个测试开始后执行,只执行一次
setUpClass_func_setUpClass
.....
tearDownClass_整个测试完成后执行,只执行一次
tearDownClass_func_setUpClass
setUpClass初始化的值在函数中无法改变,应该是clsself的指向不一样

setUp和setUpClass

  • 每个用例都要独立,也就是每个用执行时,都会重开设备,就使用setup,比如appium中的driver
  • 若想复用某个初始化条件,单不期望每个用例都重启再打开,可以使用setUpClass
  • 在使用appium时,用setUp频繁初始化driver造成执行用例时间太长,可以使用setUpClass初始化driver,结合launch-app达到每个用例不重启,同时解决依赖关系的问题
  • 不同的运行unittest的方式

    unittest.main()
    
     # 构造测试集
     suite = unittest.TestSuite()
     suite.addTest(MyUnittest("test_add"))
     suite.addTest(MyUnittest("test_subtract"))
     # 执行测试
     runner = unittest.TextTestRunner()
     runner.run(suite)
    
    # TestLoader 用来加载TestCase到TestSuite中的,其中有几个  loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite实例
    # TestSuite 测试套件集合,最终将传递给testRunner进行测试执行
    # TextTestRunner 用来执行测试用例,将测试结果保存在TextTestResult中
    # 此用法可以同时测试多个类
    suite1 = unittest.TestLoader().loadTestsFromTestCase(MyUnittest)
    suite2 = unittest.TestLoader().loadTestsFromTestCase(MyUnittest1)
    suite = unittest.TestSuite([suite1, suite2 ])
    unittest.TextTestRunner(verbosity=2).run(suite)
    
    # 进入到测试目录
    os.chdir(".//ut_module")
    os.system("python -m unittest -v ut1_test")
    

    方式五,直接到终端中输入命令

    # 测试某个类下的所有函数
    D:\project\ut>python -m unittest -v  ut_module.ut1_test
    # 测试类中某一个函数
    D:\project\ut>python -m unittest -v ut_module.ut1_test.MyUnittest.test_add
    # 执行模块下所有test结尾的文件
    D:\project\ut>python -m unittest discover D:\project\ut\ut_module "*_test.py"
    # 同时执行多个测试类下的测试函数
    D:\project\ut>python -m unittest  ut_module.ut1_test.MyUnittest.test_add  ut_module.ut2_test.MyUnittest.test_subtract1
    

    unittest的条件装饰器的使用

    class MyTestCase(unittest.TestCase):
        @unittest.skip("demonstrating skipping")
        def test_nothing(self):
            self.fail("shouldn't happen")
        @unittest.skipIf(mylib.__version__ < (1, 3),
                         "not supported in this library version")
        def test_format(self):
            # Tests that work for only a certain version of the library.
        @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
        def test_windows_support(self):
            # windows specific testing code
        def test_maybe_skipped(self):
            if not external_resource_available():
                self.skipTest("external resource not available")
            # test code that depends on the external resource
       @unittest.expectedFailure
        def test_fail(self):
            self.assertEqual(1, 0, "broken")
    @unittest.skip("showing class skipping")
    class MySkippedTestCase(unittest.TestCase):
        def test_not_run(self):
    @unittest.skip(reason)
    跳过被此装饰器装饰的测试。 reason 为测试被跳过的原因。 @unittest.skipIf(condition, reason)
    当 condition 为真时,跳过被装饰的测试。 @unittest.skipUnless(condition, reason)
    跳过被装饰的测试,除非 condition 为真。
    @unittest.expectedFailure
    把测试标记为预计失败。如果测试不通过,会被认为测试成功;如果测试通过了,则被认为是测试失败。 exception unittest.SkipTest(reason)
    引发此异常以跳过一个测试。
  • 在使用unittest时,希望testcase传参给unittest,下面时用appium的一个伪代码
  • class ParametrizedTestCase(unittest.TestCase):
        """ TestCase classes that want to be parametrized should  
            inherit from this class.  
        def __init__(self, methodName='runTest', param=None):
            super(ParametrizedTestCase, self).__init__(methodName)
          # 得到testcase传来的参数
            global devicess
            devicess = param
        @classmethod
        def setUpClass(cls):
            cls.driver = get_driver(devicess)
            cls.logTest = myLog().getLog(cls.devicesName)  # 每个设备实例化一个日志记录器
        def setUp(self):
        @classmethod
        def tearDownClass(cls):
            cls.driver.close_app()
            cls.driver.quit()
        def tearDown(self):
        @staticmethod
        def parametrize(testcase_klass, param=None):
            testloader = unittest.TestLoader()
            testnames = testloader.getTestCaseNames(testcase_klass)
            suite = unittest.TestSuite()
            for name in testnames:
                suite.addTest(testcase_klass(name, param=param))
            return suite
    class HomeTest(ParametrizedTestCase):
        def testFirstOpen(self):
            app = {"logTest": self.logTest, "driver": self.driver, "path": PATH("../yamls/home/firstOpen.yaml"),
                   "device": self.devicesName, "caseName": sys._getframe().f_code.co_name}
            page = FirstOpenPage(app)
            page.operate()
            page.checkPoint()
        @classmethod
        def setUpClass(cls):
            super(HomeTest, cls).setUpClass()
        @classmethod
        def tearDownClass(cls):
            super(HomeTest, cls).tearDownClass()
    if __name__ == '__main__':
        devices = {"设备信息"}
        suite = unittest.TestSuite()
        suite.addTest(ParametrizedTestCase.parametrize(HomeTest, param=devices))
        unittest.TextTestRunner(verbosity=2).run(suite)
    
  • pip install ddt
  • 使用json

  • 新建文件 test_data_list.json:
  • "Hello", "Goodbye"
  • 新建文件 test_data_dict.json:
  • "unsorted_list": [ 10, 12, 15 ], "sorted_list": [ 15, 12, 50 ]
    import unittest
    from ddt import ddt, file_data
    from ddt_demo.mycode import has_three_elements,is_a_greeting
    class FooTestCase(unittest.TestCase):
        @file_data('test_data_dict.json')
        def test_file_data_json_dict(self, value):
            self.assertTrue(has_three_elements(value))
        @file_data('test_data_list.json')
        def test_file_data_json_list(self, value):
            self.assertTrue(is_a_greeting(value))
    if __name__=='__main__':
        unittest.main(verbosity=2
    

    使用yaml文件

    import unittest
    from ddt import ddt, file_data
    from ddt_demo.mycode import has_three_elements,is_a_greeting
    class FooTestCase(unittest.TestCase):
        @file_data('test_data_dict.yaml')
        def test_file_data_yaml_dict(self, value):
            self.assertTrue(has_three_elements(value))
        @file_data('test_data_list.yaml')
        def test_file_data_yaml_list(self, value):