【unittest单元测试框架】(2)关于 unittest 还需要知道的
关于 unittest 还需要知道的
1、测试用例的执行顺序
测试用例的执行顺序涉及多个层级:多个测试目录 > 多个测试文件 > 多个测试类 >多个测试方法(测试用例)。unittest 提供的 main()方法和 discover()方法是按照什么顺序查找测试用例的呢? 我们先运行一个例子,再解释 unittest 的执行策略。# -*- coding:utf-8 -*- # filename: test_unittest_order.py # author: hello.yin # create time: 2021/11/16 15:25 import unittest class Test_B(unittest.TestCase): def setUp(self): print("class B") def test_c(self): print("ccc") def test_a(self): print("aaa") class Test_A(unittest.TestCase): def setUp(self): print("class A") def test_b(self): print("bbb") if __name__ == "__main__": unittest.main(verbosity=2)
执行结果:
Testing started at 15:44 ... C:\Users\yzp\PycharmProjects\base_practice\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm 2018.2\helpers\pycharm\_jb_unittest_runner.py" --path D:/00test/base_practice/practice/test_unittest_order.py Launching unittests with arguments python -m unittest D:/00test/base_practice/practice/test_unittest_order.py in D:\00test\base_practice\practice Ran 3 tests in 0.001s OK class A bbb class B aaa class B ccc Process finished with exit code 0无论执行多少次,结果都是一样的。通过上面的结果,相信你已经找到 main()方法执行测试用例的规律了。 因为unittest默认根据ASCII码的顺序加载测试用例的(数字与字母的顺序为0~9,A~Z,a~z),所以 TestAdd 类会优先于 TestBdd 类被执行,test_aaa()方法会优先于 test_ccc()方法被执行,也就是说,它并不是按照测试用例的创建顺序从上到下执行的。 discover()方法和 main()方法的执行顺序是一样的。对于测试目录与测试文件来说,上面的规律同样适用。test_aaa.py 文件会优先于 test_bbb.py 文件被执行。所以,如果想让某个测试文件先执行,可以在命名上加以控制。 除命名外,有没有其他办法控制测试用例的执行顺序呢?答案是肯定的,前面也有介绍,我们可以声明测试套件 TestSuite 类,通过 addTest()方法按照一定的顺序来加载测试用例。
修改上面的例子如下。
# -*- coding:utf-8 -*- # filename: test_unittest_order.py # author: hello.yin # create time: 2021/11/16 15:25 import unittest class Test_B(unittest.TestCase): def setUp(self): print("class B") def test_c(self): print("ccc") def test_a(self): print("aaa") class Test_A(unittest.TestCase): def setUp(self): print("class A") def test_b(self): print("bbb") if __name__ == "__main__": suit = unittest.TestSuite() suit.addTest(Test_A("test_b")) suit.addTest(Test_B("test_a")) suit.addTest(Test_B("test_c")) runner = unittest.TextTestRunner(verbosity=2) runner.run(suit)
执行结果:
Testing started at 15:58 ... C:\Users\yzp\PycharmProjects\base_practice\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm 2018.2\helpers\pycharm\_jb_unittest_runner.py" --path D:/00test/base_practice/practice/test_unittest_order.py Launching unittests with arguments python -m unittest D:/00test/base_practice/practice/test_unittest_order.py in D:\00test\base_practice\practice Ran 3 tests in 0.002s OK class A bbb class B aaa class B ccc Process finished with exit code 0现在的执行顺序与 addTest()方法加载测试用例的顺序相同。不过,当测试用例非常多时,不推荐用这种方法创建测试套件,原因前面也有说明,最好的方法是通过命名控制执行顺序。如果测试用例在设计时不产生相互依赖,那么测试用例的执行顺序就没那么重要了。
2、执行多级目录的测试用例
当测试用例的数量达到一定量级时,就要考虑目录划分,比如规划如下测试目录。 test_project ├──/test_case/ │ ├── test_bbb/ │ │ ├── test_ccc/ │ │ │ └── test_c.py │ │ └── test_b.py │ ├── test_ddd/ │ │ └── test_d.py │ └── test_a.py └─ run_tests.py 对于上面的目录结构,如果将 discover()方法中的 start_dir 参数定义为“./test_case”目录,那么只能加载 test_a.py 文件中的测试用例。如何让 unittest 查找 test_case/下子目录中 的测试文件呢?方法很简单,就是在每个子目录下放一个__init__.py 文件。__init__.py 文件的作用是将一个目录标记成一个标准的 Python 模块。3、跳过测试和预期失败
在运行测试时,有时需要直接跳过某些测试用例,或者当测试用例符合某个条件时跳过测试,又或者直接将测试用例设置为失败。unittest 提供了实现这些需求的装饰器。unittest.skip(reason)无条件地跳过装饰的测试,需要说明跳过测试的原因。
unittest.skipIf(condition, reason)
如果条件为真,则跳过装饰的测试。
unittest.skipUnless(condition, reason)当条件为真时,执行装饰的测试。
unittest.expectedFailure()
不管执行结果是否失败,都将测试标记为失败。
# -*- coding:utf-8 -*- # filename:test_skip.py # author:hello.yin # create time:2021/11/16 16:15 import unittest class SkipTest(unittest.TestCase): @unittest.skip("直接跳过测试!") def test_a_skip(self): print("测试unittest.skip装饰器") @unittest.skipUnless(3 > 2, "当3大于2时执行测试!") def test_b_skipUnless(self): print("测试unittest.skipUnless装饰器") @unittest.skipIf(3 > 2, "当3大于2时跳过测试!") def test_c_skipIf(self): print("测试unittest.skipIf装饰器") @unittest.expectedFailure def test_d_expectedFailure(self): self.assertEqual(2, 2) if __name__ == "__main__": unittest.main(verbosity=2)
执行结果:
Testing started at 16:25 ... C:\Users\yzp\PycharmProjects\base_practice\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm 2018.2\helpers\pycharm\_jb_unittest_runner.py" --path D:/00test/base_practice/practice/test_skip.py Ran 4 tests in 0.002s Launching unittests with arguments python -m unittest D:/00test/base_practice/practice/test_skip.py in D:\00test\base_practice\practiceFAILED (skipped=2, unexpected successes=1) Skipped: 直接跳过测试! 测试unittest.skipUnless装饰器 Skipped: 当3大于2时跳过测试! Failure Test should not succeed since it's marked with @unittest.expectedFailure Process finished with exit code 1上面的例子创建了四条测试用例。 第一条测试用例通过@unittest.skip()装饰,直接跳过测试。 第二条测试用例通过@unittest.skipUnless()装饰,当条件为真时执行测试;3>2 条件为真(True),执行测试。 第三条测试用例通过@unittest.skipIf()装饰,当条件为真时跳过测试;3>2 条件为真(True),所以跳过测试。 第四条测试用例通过@unittest.expectedFailure 装饰,不管执行结果是否失败,都将测 试标记为失败,但不会抛出失败信息。 当然,这些方法同样适用于测试类,只需将它们针对测试类装饰即可
import unittest @unittest.skip("直接跳过,不测试该测试类") class MyTest(unittest.TestCase): # ……
4、Fixture
我们可以把 Fixture 看作夹心饼干外层的两片饼干,这两片饼干就是 setUp/tearDown,中间的奶油就是测试用例。除此之外,unittest 还提供了更大范围的 Fixture,如测试类和模块的 Fixture。 setUpModule/tearDownModule:在整个模块的开始与结束时被执行。 setUpClass/tearDownClass:在测试类的开始与结束时被执行。 setUp/tearDown:在测试用例的开始与结束时被执行。 需要注意的是,setUpClass/tearDownClass 为类方法,需要通过@classmethod 进行装饰。另外,方法的参数为 cls。其实,cls 与 self 并没有什么本质区别,都只表示方法的第一个参数。# -*- coding:utf-8 -*- # filename: test_fixtrue.py # author: hello.yin # create time: 2021/11/16 16:37 import unittest def setUpModule(): print("======setUpModule======") def tearDownModule(): print("=======tearDownModule========") class TestFixtrue(unittest.TestCase): @classmethod def setUpClass(cls): print("======setUpClass======") @classmethod def tearDownClass(cls): print("======tearDownClass===========") def setUp(self): print("====setUp========") def tearDown(self): print("======tearDown=======") def test_case1(self): print("testcase1") def test_case2(self): print("testcase2") if __name__ == "__main__": unittest.main(verbosity=2)
执行结果:
Testing started at 16:47 ... C:\Users\yzp\PycharmProjects\base_practice\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm 2018.2\helpers\pycharm\_jb_unittest_runner.py" --path D:/00test/base_practice/practice/test_fixtrue.py Launching unittests with arguments python -m unittest D:/00test/base_practice/practice/test_fixtrue.py in D:\00test\base_practice\practice Ran 2 tests in 0.002s OK ======setUpModule====== ======setUpClass====== ====setUp======== testcase1 ======tearDown======= ====setUp======== testcase2 ======tearDown======= ======tearDownClass=========== =======tearDownModule======== Process finished with exit code 0