一文掌握python单元测试unittest(二)
接上篇:https://blog.csdn.net/qq_38120851/article/details/141642215
目录
四、参数化测试
1、使用 subTest
2、使用装饰器
3)使用第三方库parameterized
五、跳过测试
1、使用 unittest.skip() 或 unittest.skipIf() 装饰器:
2、使用 setUp() 方法中的断言来跳过整个测试类:
3、在测试方法内部使用 unittest.TestCase.skipTest() 方法:
六、预期失败
七、自定义测试结果类
八、参数化测试进阶,parameterized 库
1、什么是 parameterized?
2、安装 parameterized
3、使用类属性定义参数集:parameterized.expand
4、使用外部文件加载参数集
1) 使用 CSV 文件加载参数集
2) 使用yaml加载参数集,推荐使用yaml, 因为yaml结构更清晰,方便维护。
5、使用函数生成参数集
6、使用字典参数集
7、使用param和name_func提供更多的信息,比如参数的名称、描述。
8、使用类级别的参数化
1)使用属性元组和元组列表
2)使用字典列表
3)使用 class_name_func 来动态生成测试类的名字
九、参数化测试进阶,ddt 库
1、通过 pip 安装:
2、data:为测试函数提供多个数据集。
1)data传参list(同理如果传tuple,即将相应的list换成tuple):
2)data传参dict:
3、unpack:解包
4、file_data:从文件中加载测试数据。每个用例的key会添加到用例名中去。
1)加载json文件
2)加载yaml文件
5、named_data:为每组测试数据提供一个名称,这有助于识别测试案例。
四、参数化测试
参数化测试是一种常见的需求,特别是在需要针对一组数据多次运行相同的测试逻辑时。unittest
本身并没有直接支持参数化测试的功能,但是可以通过一些技巧来实现这一点。
通常用第三方库pytest或parameterized来进行参数化测试。
1、使用 subTest
unittest
提供了 subTest
方法来实现参数化测试,可以在测试方法内部使用它来创建子测试,每个子测试都有一个特定的上下文。即结合循环来间接实现参数化测试。
import unittestclass TestMultiplication(unittest.TestCase):def test_multiply(self):cases = [(2, 3, 6),(2, -3, -6),(-2, -3, 6)]for x, y, expected in cases:with self.subTest(x=x, y=y):self.assertEqual(x * y, expected)print(f'Test passed for x={x}, y={y}, expected={expected}')if __name__ == "__main__":unittest.main()
2、使用装饰器
缺点是实际是运行了3个用例,但是控制台上输出是Ran 1 tests in 0.000s,统计测试输出报告时比较麻烦。
import unittestclass parameterized(object):@staticmethoddef expand(argvalues):def decorator(func):def wrapper(*args):for arg in argvalues:func(*(args + arg))return wrapperreturn decoratorclass TestMathFunctions(unittest.TestCase):@parameterized.expand([(2, 3, 5),(3, 4, 7),(1, 9, 10)])def test_add(self, a, b, expected):result = a + bself.assertEqual(result, expected)print("Test add with a={}, b={}, expected={}".format(a, b, expected))if __name__ == '__main__':unittest.main()
但是控制台上输出是Ran 1 test in 0.000s
3)使用第三方库parameterized
import unittest
from parameterized import parameterizedclass TestMathFunctions(unittest.TestCase):@parameterized.expand([(1, 2, 3),(4, 5, 9),(10, -2, 8)])def test_add(self, a, b, expected):result = a + bself.assertEqual(result, expected)if __name__ == "__main__":unittest.main()
控制台上输出是Ran 3 tests in 0.000s
五、跳过测试
1、使用 unittest.skip()
或 unittest.skipIf()
装饰器:
unittest.skip(reason)
:可以无条件地跳过一个测试方法,并给出跳过的理由。unittest.skipIf(condition, reason)
:只有当给定的条件为True
时才会跳过测试方法。
import unittestclass TestSkipExample(unittest.TestCase):@unittest.skip("无条件跳过这个测试")def test_always_skipped(self):self.fail("应该不会执行到这里")@unittest.skipIf(True, "条件为真时跳过这个测试")def test_skipped_if_true(self):self.fail("条件为真时不应该执行到这里")@unittest.skipIf(False, "条件为假时跳过这个测试")def test_not_skipped_if_false(self):self.assertTrue(True) # 这个测试将会被执行if __name__ == '__main__':unittest.main()
2、使用 setUp()
方法中的断言来跳过整个测试类:
- 如果
setUp()
方法中抛出异常(例如通过assert
),那么整个测试类将不会被执行。
import unittestclass TestSetupSkipExample(unittest.TestCase):def setUp(self):self.skipTest("setUp 中的断言失败,跳过整个测试类") # 这行代码将导致整个测试类被跳过def test_example_one(self):self.assertTrue(True)def test_example_two(self):self.assertTrue(True)if __name__ == '__main__':unittest.main()
3、在测试方法内部使用 unittest.TestCase.skipTest()
方法:
- 如果在一个测试方法内部想要跳过后续的测试步骤,可以使用
self.skipTest(reason)
。
import unittestclass TestSkipInMethodExample(unittest.TestCase):def test_example(self):self.skipTest("测试方法内部决定跳过")self.assertTrue(False) # 这行代码不会被执行def test_example_2(self):self.assertTrue(True) # 这行代码不会被执行if __name__ == '__main__':unittest.main()
六、预期失败
使用 expectedFailure
装饰器来标记那些预期会失败的测试用例。这在开发过程中非常有用,特别是当正在重构代码或逐步改进现有功能时。这样,可以跟踪哪些测试在当前状态下应该是失败的,直到它们最终通过为止。
import unittestclass TestExpectedFailures(unittest.TestCase):def test_known_failure(self):""" 这个测试应该失败,因为我们还没有修复这个 bug。 """self.assertEqual(1, 2) # 这个断言应该失败@unittest.expectedFailuredef test_known_failure_with_decorator(self):""" 使用装饰器标记已知的失败测试。 """self.assertEqual(1, 2) # 这个断言应该失败def test_known_failure_without_decorator(self):""" 这个测试应该失败,但我们没有使用装饰器标记。 """self.assertEqual(1, 2) # 这个断言应该失败if __name__ == "__main__":unittest.main()
七、自定义测试结果类
通过继承 unittest.TextTestResult
类来自定义测试结果的处理方式。这样做可以增加更多的定制功能,比如记录额外的信息、更改输出格式等。
import unittest
import sysclass CustomTestResult(unittest.TextTestResult):"""自定义测试结果类,扩展了unittest.TextTestResult以添加额外的测试结果输出功能."""def __init__(self, stream, descriptions, verbosity):"""初始化自定义测试结果,设置输出流、描述和详细程度."""super().__init__(stream, descriptions, verbosity)def startTest(self, test):"""在测试开始时调用,打印开始测试的信息."""super().startTest(test)print(f"Starting test: {test}")def addSuccess(self, test):"""在测试成功时调用,打印测试通过的信息."""super().addSuccess(test)print(f"Test {test} passed.")def addFailure(self, test, err):"""在测试失败时调用,打印测试失败的信息及错误."""super().addFailure(test, err)print(f"Test {test} failed: {err}")def addError(self, test, err):"""在测试出错时调用,打印测试出错的信息及错误."""super().addError(test, err)print(f"Test {test} err