1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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()
执行结果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 ok test_subtract (ut_module.ut1_test.MyUnittest) ... setUp_每运行一次用例都会执行一次 setUp_name_hi 测试函数:test_subtract tearDown_每结束一次用例都会执行一次 tearDown_name_test_subtract ok tearDownClass_整个测试完成后执行,只执行一次 tearDownClass_func_setUpClass ---------------------------------------------------------------------- Ran 2 tests in 0.003s OK
测试分析 结果分析 setUp初始化的值可以改变 1 2 3 4 5 setUp_name_hi 测试函数:test_add setUpClass_cls.func_setUpClass tearDown_每结束一次用例都会执行一次 tearDown_name_test_add
setUp
中的self.name
的值,在测试函数中是可以被改变的,一开始name
为hi,后面为test_add
setUpClass的的值无法改变 1 2 3 4 5 6 7 8 9 10 在def setUpClass赋值 self.func = "setUpClass" 在 def test_subtract中赋值 self.func = "test_subtract" # 执行结果 setUpClass_整个测试开始后执行,只执行一次 setUpClass_func_setUpClass ..... tearDownClass_整个测试完成后执行,只执行一次 tearDownClass_func_setUpClass
setUpClass
初始化的值在函数中无法改变,应该是cls
和self
的指向不一样
setUp和setUpClass
每个用例都要独立,也就是每个用执行时,都会重开设备,就使用setup
,比如appium
中的driver
若想复用某个初始化条件,单不期望每个用例都重启再打开,可以使用setUpClass
在使用appium
时,用setUp
频繁初始化driver
造成执行用例时间太长,可以使用setUpClass
初始化driver,结合launch-app 达到每个用例不重启,同时解决依赖关系的问题
不同的运行unittest的方式 方式一
方式二 1 2 3 4 5 6 7 # 构造测试集 suite = unittest.TestSuite() suite.addTest(MyUnittest("test_add")) suite.addTest(MyUnittest("test_subtract")) # 执行测试 runner = unittest.TextTestRunner() runner.run(suite)
方式三 1 2 3 4 5 6 7 8 # 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)
方式四 1 2 3 # 进入到测试目录 os.chdir(".//ut_module") os.system("python -m unittest -v ut1_test")
方式五,直接到终端中输入命令 1 2 3 4 5 6 7 8 9 # 测试某个类下的所有函数 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的条件装饰器的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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. pass @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_windows_support(self): # windows specific testing code pass def test_maybe_skipped(self): if not external_resource_available(): self.skipTest("external resource not available") # test code that depends on the external resource pass @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): pass
@unittest.skip(reason)
跳过被此装饰器装饰的测试。 reason 为测试被跳过的原因。
@unittest.skipIf(condition, reason)
当 condition 为真时,跳过被装饰的测试。
@unittest.skipUnless(condition, reason)
跳过被装饰的测试,除非 condition 为真。
@unittest.expectedFailure
把测试标记为预计失败。如果测试不通过,会被认为测试成功;如果测试通过了,则被认为是测试失败。
exception unittest.SkipTest(reason)
引发此异常以跳过一个测试。
参数化
在使用unittest
时,希望testcase
传参给unittest
,下面时用appium
的一个伪代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 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): pass cls.driver = get_driver(devicess) cls.logTest = myLog().getLog(cls.devicesName) # 每个设备实例化一个日志记录器 def setUp(self): pass @classmethod def tearDownClass(cls): cls.driver.close_app() cls.driver.quit() pass def tearDown(self): pass @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)
ddt
使用json
新建文件 test_data_list.json:1 2 3 4 [ "Hello", "Goodbye" ]
新建文件 test_data_dict.json:1 2 3 4 { "unsorted_list": [ 10, 12, 15 ], "sorted_list": [ 15, 12, 50 ] }
实例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import unittest from ddt import ddt, file_data from ddt_demo.mycode import has_three_elements,is_a_greeting @ddt 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文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import unittest from ddt import ddt, file_data from ddt_demo.mycode import has_three_elements,is_a_greeting @ddt 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): self.assertTrue(is_a_greeting(value)) if __name__=='__main__': unittest.main(verbosity=2)