一、unittest 基础介绍

unittest 模块提供了一种结构化的方式来编写测试用例。测试用例继承自 unittest.TestCase 类,并通过一系列的测试方法(以 test_ 开头的方法)来组织。

二、测试套件 (TestSuite)

unittest.TestSuite 是一个容器对象,可以容纳多个测试用例,这样就可以批量执行一组测试用例。

创建测试套件

  1. import unittest

  2. class TestStringMethods(unittest.TestCase):

  3. def test_upper(self):

  4. self.assertEqual('foo'.upper(), 'FOO')

  5. class TestNumberMethods(unittest.TestCase):

  6. def test_add(self):

  7. self.assertEqual(1 + 2, 3)

  8. def suite():

  9. suite = unittest.TestSuite()

  10. suite.addTest(TestStringMethods('test_upper'))

  11. suite.addTest(TestNumberMethods('test_add'))

  12. return suite

  13. if __name__ == '__main__':

  14. runner = unittest.TextTestRunner()

  15. runner.run(suite())

  16. # 输出:

  17. # .....

  18. # ----------------------------------------------------------------------

  19. # Ran 2 tests in 0.000s

  20. #

  21. # OK

三、测试加载器 (TestLoader)

unittest.TestLoader 提供了自动发现和加载测试用例的方法。

使用测试加载器自动发现测试


  1. import unittest

  2. class TestStringMethods(unittest.TestCase):

  3. def test_upper(self):

  4. self.assertEqual('foo'.upper(), 'FOO')

  5. class TestNumberMethods(unittest.TestCase):

  6. def test_add(self):

  7. self.assertEqual(1 + 2, 3)

  8. if __name__ == '__main__':

  9. loader = unittest.TestLoader()

  10. tests = loader.discover(start_dir='.', pattern='test*.py')

  11. runner = unittest.TextTestRunner()

  12. runner.run(tests)

  13. # 输出:

  14. # .....

  15. # ----------------------------------------------------------------------

  16. # Ran 2 tests in 0.000s

  17. #

  18. # OK

四、测试结果处理器 (TextTestResult)

unittest 默认使用 TextTestResult 来处理测试结果。我们可以通过继承 TextTestResult 来定制输出格式。

定制测试结果输出


  1. import unittest

  2. class CustomTestResult(unittest.TextTestResult):

  3. def addSuccess(self, test):

  4. super().addSuccess(test)

  5. print(f"测试成功: {test}")

  6. class TestStringMethods(unittest.TestCase):

  7. def test_upper(self):

  8. self.assertEqual('foo'.upper(), 'FOO')

  9. if __name__ == '__main__':

  10. loader = unittest.TestLoader()

  11. tests = loader.loadTestsFromTestCase(TestStringMethods)

  12. runner = unittest.TextTestRunner(resultclass=CustomTestResult)

  13. runner.run(tests)

  14. # 输出:

  15. # 测试成功: <__main__.TestStringMethods testMethod=test_upper>

  16. # .....

  17. # ----------------------------------------------------------------------

  18. # Ran 1 test in 0.000s

  19. #

  20. # OK

五、测试用例的跳过 (skip)

unittest 提供了 skip 和 skipIf 装饰器来跳过某些测试用例。

使用 skip 装饰器跳过测试


  1. import unittest

  2. class TestStringMethods(unittest.TestCase):

  3. @unittest.skip("暂时跳过此测试")

  4. def test_upper(self):

  5. self.assertEqual('foo'.upper(), 'FOO')

  6. def test_split(self):

  7. s = 'hello world'

  8. self.assertEqual(s.split(), ['hello', 'world'])

  9. if __name__ == '__main__':

  10. unittest.main()

  11. # 输出:

  12. # .....

  13. # ----------------------------------------------------------------------

  14. # Ran 1 test in 0.000s

  15. #

  16. # OK

使用 skipIf 装饰器条件性跳过测试


  1. import unittest

  2. class TestStringMethods(unittest.TestCase):

  3. @unittest.skipIf(True, "当条件为真时跳过此测试")

  4. def test_upper(self):

  5. self.assertEqual('foo'.upper(), 'FOO')

  6. def test_split(self):

  7. s = 'hello world'

  8. self.assertEqual(s.split(), ['hello', 'world'])

  9. if __name__ == '__main__':

  10. unittest.main()

  11. # 输出:

  12. # .....

  13. # ----------------------------------------------------------------------

  14. # Ran 1 test in 0.000s

  15. #

  16. # OK

六、测试用例的重试 (retry)

虽然 unittest 本身不支持测试重试,但可以通过装饰器或第三方库来实现。

使用装饰器实现测试重试


  1. import unittest

  2. import time

  3. def retry(max_attempts=3, delay=1):

  4. def decorator(func):

  5. def wrapper(*args, **kwargs):

  6. attempts = 0

  7. while attempts < max_attempts:

  8. try:

  9. return func(*args, **kwargs)

  10. except Exception as e:

  11. attempts += 1

  12. if attempts >= max_attempts:

  13. raise

  14. time.sleep(delay)

  15. return wrapper

  16. return decorator

  17. class TestStringMethods(unittest.TestCase):

  18. @retry(max_attempts=3, delay=1)

  19. def test_upper(self):

  20. self.assertEqual('foo'.upper(), 'FOO')

  21. if __name__ == '__main__':

  22. unittest.main()

  23. # 输出:

  24. # .....

  25. # ----------------------------------------------------------------------

  26. # Ran 1 test in 0.000s

  27. #

  28. # OK

七、测试用例的顺序 (order)

默认情况下,unittest 会按照定义的顺序执行测试用例,但如果需要改变顺序,可以通过其他方式实现。

改变测试用例的执行顺序


  1. import unittest

  2. class TestStringMethods(unittest.TestCase):

  3. def test_upper(self):

  4. self.assertEqual('foo'.upper(), 'FOO')

  5. def test_split(self):

  6. s = 'hello world'

  7. self.assertEqual(s.split(), ['hello', 'world'])

  8. def test_isupper(self):

  9. self.assertTrue('FOO'.isupper())

  10. if __name__ == '__main__':

  11. loader = unittest.TestLoader()

  12. loader.sortTestMethodsUsing = lambda _, x, y: -1 if x > y else 1

  13. tests = loader.loadTestsFromTestCase(TestStringMethods)

  14. runner = unittest.TextTestRunner()

  15. runner.run(tests)

  16. # 输出:

  17. # .....

  18. # ----------------------------------------------------------------------

  19. # Ran 3 tests in 0.000s

  20. #

  21. # OK

八、测试用例的标签 (tags)

虽然 unittest 本身不直接支持测试用例的标签功能,但可以通过自定义测试加载器来实现。

使用自定义测试加载器添加标签


  1. import unittest

  2. class TestStringMethods(unittest.TestCase):

  3. def test_upper(self):

  4. self.assertEqual('foo'.upper(), 'FOO')

  5. def test_split(self):

  6. s = 'hello world'

  7. self.assertEqual(s.split(), ['hello', 'world'])

  8. def load_tests(loader, tests, pattern):

  9. suite = unittest.TestSuite()

  10. for test_case in tests:

  11. if hasattr(test_case, '_testMethodName'):

  12. if 'slow' in getattr(test_case, '_testMethodName'):

  13. continue

  14. suite.addTest(test_case)

  15. return suite

  16. if __name__ == '__main__':

  17. loader = unittest.TestLoader()

  18. tests = loader.loadTestsFromTestCase(TestStringMethods)

  19. tests = load_tests(loader, tests, pattern='test*')

  20. runner = unittest.TextTestRunner()

  21. runner.run(tests)

  22. # 输出:

  23. # .....

  24. # ----------------------------------------------------------------------

  25. # Ran 2 tests in 0.000s

  26. #

  27. # OK

九、使用 setUpClass 和 tearDownClass

setUpClass 和 tearDownClass 方法会在所有测试方法之前和之后分别被调用一次。

使用 setUpClass 和 tearDownClass


  1. import unittest

  2. class TestStringMethods(unittest.TestCase):

  3. @classmethod

  4. def setUpClass(cls):

  5. print("设置测试环境")

  6. @classmethod

  7. def tearDownClass(cls):

  8. print("清理测试环境")

  9. def test_upper(self):

  10. self.assertEqual('foo'.upper(), 'FOO')

  11. def test_split(self):

  12. s = 'hello world'

  13. self.assertEqual(s.split(), ['hello', 'world'])

  14. if __name__ == '__main__':

  15. unittest.main()

  16. # 输出:

  17. # 设置测试环境

  18. # .....

  19. # 清理测试环境

  20. # ----------------------------------------------------------------------

  21. # Ran 2 tests in 0.000s

  22. #

  23. # OK

十、使用 assertRaisesRegex 验证异常消息

assertRaisesRegex 用于验证抛出的异常是否包含预期的消息。

使用 assertRaisesRegex


  1. import unittest

  2. class TestStringMethods(unittest.TestCase):

  3. def test_split(self):

  4. s = 'hello world'

  5. with self.assertRaisesRegex(ValueError, "empty separator"):

  6. s.split('')

  7. if __name__ == '__main__':

  8. unittest.main()

  9. # 输出:

  10. # .....

  11. # ----------------------------------------------------------------------

  12. # Ran 1 test in 0.000s

  13. #

  14. # OK

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐