unittest 提供了一个测试用例类(TestCase),作为我们后续编写测试用例的基类。
在编写测试用例的时候,需要注意以下几个事项:
- 定义测试用例类:
- 名称:以 Test 为前缀命名
- 基类:继承自 TestCase 基类
- 定义测试方法
- 常规测试方法:以 test 为前缀命名
- 特殊测试方法:runTest()
- 在创建测试用例实例对象的时候,构造方法中需要传入一个方法名称(methodName)参数,而这个方法名称参数又提供了一个默认值 runTest,即如果你不提供任何方法名称,unittest 会自动在当前用例中查找一个名称叫 “runTest” 的方法并执行。
- 每个测试用例类中可以且仅可以提供一个 runTest() 测试方法,作为该测试用例的默认执行方法。
- 使用断言
- 在 unittest 框架中,由 TestCase 类提供了很多断言方法,基本以 assertXxxx() 方法命名,用法一般为 self.assertEqual(....) 形式
- unittest 支持对一些特殊类型,如列表,字典等进行自定义比较断言,代替原有的assertEqual() 断言方法
- 也可以直接使用 Python 自带的断言方法,类似 assert 1 == 1 形式
1. TestCase 类介绍
官方说明:unittest --- 单元测试框架 — Python 3.11.8 文档
TestCase 类中提供的主要方法包括:
类别 | 方法名称 |
运行测试 | run() |
测试固件 | setUp()、tearDown()、setUpClass()、tearDownClass() |
跳过测试 | skipTest()、fail() |
测试断言 | assertEqual()、assertNotEqual()、assertTrue()、assertFalse()、assertRaises()、assertIn()、assertIs()、assertLess() ...... |
2. 关于 runTest() 方法
文心一言:什么时候会使用默认值 'runTest' 测试方法
在 unittest 框架中,默认值 'runTest' 通常不会被直接使用,因为在实际的测试用例中,你会为每个具体的测试场景编写单独的测试方法,并且这些方法通常以 test_ 开头(这是一个常见的命名约定,但不是强制的)。然而,有几种情况你可能会间接地或特殊地遇到或使用到默认值 'runTest':
1. 没有显式定义测试方法:如果你创建了一个继承自 unittest.TestCase 的子类,但忘记或没有定义任何以 test_ 开头的测试方法,并且也没有覆盖 runTest 方法,那么当你尝试运行这个测试类时,将会调用默认的 runTest 方法(它实际上并没有做任何事情,因为它在 unittest.TestCase 中是一个占位符)。这通常会导致一个错误或失败,因为默认的 runTest 方法并不包含任何测试逻辑。
2. 自定义测试执行逻辑:在某些高级用法中,你可能会覆盖 runTest 方法以定义自定义的测试执行逻辑。例如,你可能想要在一个方法中执行多个子测试,或者在运行时根据某些条件动态地决定执行哪些测试。通过覆盖 runTest 方法,你可以实现这种自定义行为。但是,这通常是在你知道自己在做什么并且有特殊需求的情况下才会这么做。
3. 与测试框架集成:在某些情况下,你可能正在与另一个测试框架或工具集成,该框架或工具使用 unittest 作为其后端,并且它依赖于 runTest 方法作为默认的测试执行点。虽然这种情况不常见,但了解这一点有助于你理解为什么在某些环境中可能会使用到 runTest 方法。
4. 加载和执行特定的测试:虽然不常见,但你可以通过编程方式加载一个测试类,并指定要运行的测试方法(包括 runTest)。这可以通过使用 unittest.TestLoader 类的 loadTestsFromMethodName 方法来实现,该方法允许你指定一个类和一个方法名来加载并执行该类的特定测试方法。尽管这可以用于任何方法名,但如果你指定了 'runTest',那么就会加载并执行 runTest 方法(如果存在的话)。
总的来说,除非你有特殊的需求或正在与某个特定的测试框架或工具集成,否则在编写普通的单元测试时,你通常不会直接使用或覆盖 runTest 方法。相反,你会为每个测试场景编写单独的测试方法,并以 test_ 开头来命名这些方法。
2.1 通过 mian() 方法执行测试
- 以下测试用例中定义了三个测试方法:runTest() 、 test_m02()、other_mod()
- 通过 main() 入口执行,根据执行结果可见,只有 test_m02() 方法被执行,说明该运行自动遍历以 test 为前缀的方法并执行,这个过程中不需要自己去实例化测试用例。
class TestCase01(unittest.TestCase):
# 定义了一个默认名称的测试方法
def runTest(self):
self.assertEqual(1, 1)
# 定义了一个 test 为前缀的方法
def test_m02(self):
self.assertEqual(1, 1)
# 定义了一个其它命名方式的方法
def other_mod(self):
self.assertEqual(1, 1)
if __name__ == '__main__':
unittest.main(verbosity=2)
2.2. 自己实例化测试用例
通过测试套的方式自行实例化测试用例并执行
- 实例化的时候,不传入测试方法名称,执行默认的测试方法 (runTest),该方法被执行
- 实例化的时候,传入常用的 test 为前缀的测试方法名称(test_m02),该方法被执行
- 实例化的时候,传入非标命名的测试方法名称(other_mod),该方法被执行
即,通过测试套执行测试的时候,由于测试用例在实例化的时候,可以自行指定测试方法,因此可以不按照测试方法的命名规范来实现。
import unittest
class TestCase01(unittest.TestCase):
# unittest 提供的默认测试名称
def runTest(self):
self.assertEqual(1, 1)
# 通用测试名称,test 为前缀命名
def test_m02(self):
self.assertEqual(1, 1)
# 非通用测试名称
def other_mod(self):
self.assertEqual(1, 1)
if __name__ == '__main__':
runner = unittest.TextTestRunner(verbosity=2)
# 自己实例化三个测试用例
case_list = [TestCase01(), TestCase01("test_m02"), TestCase01("other_mod")]
suite = unittest.TestSuite(tests=case_list)
runner.run(suite)
执行结果:
3. 测试用例的自运行
与测试套一样,测试用例自己也拥有 run() 方法,可以自运行。
import unittest
class TestCase01(unittest.TestCase):
# unittest 提供的默认测试名称
def runTest(self):
self.assertEqual(1, 1)
# 通用测试名称,test 为前缀命名
def test_m02(self):
self.assertEqual(1, 1)
# 非通用测试名称
def other_mod(self):
self.assertEqual(1, 1)
if __name__ == '__main__':
print(TestCase01().run())
print(TestCase01("test_m02").run())
print(TestCase01("other_mod").run())
运行结果:每个方法都被正确执行