Python 的 unittest 模块是 Python 标准库中内置的单元测试框架,它的灵感来源于 Java 的 JUnit,支持测试自动化、共享测试配置、聚合测试报告以及独立于报告框架的测试运行。

以下是使用 unittest 模块的核心指南,从基础用法到进阶技巧。
核心概念
unittest 主要由以下几个部分组成:
- Test Case (测试用例): 测试的个体单元,通过继承
unittest.TestCase类来创建。 - Test Suite (测试套件): 用于聚合多个测试用例,以便一起运行。
- Test Runner (测试运行器): 负责执行测试并输出结果(如控制台输出或图形界面)。
- Test Fixture (测试装置): 测试的准备和清理工作(如创建临时数据库、打开文件等)。
快速上手:编写第一个测试
假设你有一个简单的函数需要测试:
被测试代码 (my_math.py):

def add(a, b):
return a + b
测试代码 (test_my_math.py):
import unittest
from my_math import add
# 1. 创建测试类,必须继承 unittest.TestCase
class TestMathFunctions(unittest.TestCase):
# 2. 测试方法必须以 'test_' 开头
def test_add_integers(self):
result = add(10, 5)
# 3. 使用断言方法检查结果
self.assertEqual(result, 15)
def test_add_strings(self):
result = add("hello ", "world")
self.assertEqual(result, "hello world")
# 4. 允许脚本直接运行
if __name__ == '__main__':
unittest.main()
常用断言方法
unittest.TestCase 提供了丰富的断言方法,如果断言失败,测试会标记为 FAIL;如果抛出异常,标记为 ERROR。
| 方法 | |
|---|---|
assertEqual(a, b) |
a == b |
assertNotEqual(a, b) |
a != b |
assertTrue(x) |
bool(x) is True |
assertFalse(x) |
bool(x) is False |
assertIs(a, b) |
a is b |
assertIsNone(x) |
x is None |
assertIn(a, b) |
a in b |
assertIsInstance(a, b) |
isinstance(a, b) |
assertRaises(Error, Func, *Args) |
调用 Func 时抛出了 Error 异常 |
assertAlmostEqual(a, b) |
round(a-b, 7) == 0 (用于浮点数比较) |
测试装置
用于在测试执行前准备环境,执行后清理环境。
setUp(): 每个测试方法运行前执行。tearDown(): 每个测试方法运行后执行。setUpClass(): 整个测试类运行前执行一次(必须加@classmethod装饰器)。tearDownClass(): 整个测试类运行后执行一次(必须加@classmethod装饰器)。
示例:

import unittest
class TestDatabase(unittest.TestCase):
def setUp(self):
# 比如在这里建立数据库连接,或创建临时文件
self.db_connection = "Connected to DB"
print("\nsetUp: 准备资源")
def tearDown(self):
# 关闭连接,删除临时文件
self.db_connection = None
print("tearDown: 清理资源")
def test_query(self):
print("Running test_query")
self.assertEqual(self.db_connection, "Connected to DB")
def test_insert(self):
print("Running test_insert")
# 这里也可以使用 self.db_connection
self.assertIsNotNone(self.db_connection)
if __name__ == '__main__':
unittest.main()
跳过测试与预期失败
有时你希望某些测试在某些条件下不执行(例如特定操作系统或未完成的功能)。
import unittest
import sys
class TestSkipping(unittest.TestCase):
@unittest.skip("演示直接跳过")
def test_nothing(self):
self.fail("我不应该运行")
@unittest.skipIf(sys.version_info < (3, 8), "需要 Python 3.8 以上版本")
def test_python_version(self):
# 仅在 Python 3.8+ 运行
self.assertTrue(True)
@unittest.skipUnless(sys.platform.startswith("win"), "仅在 Windows 上运行")
def test_windows_feature(self):
self.assertTrue(True)
@unittest.expectedFailure
def test_fail(self):
# 如果测试失败,不计入失败数,而是标记为 expected failure
self.assertEqual(1, 0)
if __name__ == '__main__':
unittest.main()
运行测试
方法 A:命令行运行(推荐)
这是最常用的方式,unittest 会自动发现测试文件。
# 运行当前目录下所有以 test*.py 命名的文件 python -m unittest discover # 运行特定模块 python -m unittest test_my_math # 运行特定类 python -m unittest test_my_math.TestMathFunctions # 运行特定方法 python -m unittest test_my_math.TestMathFunctions.test_add_integers # 显示更详细的输出 python -m unittest -v test_my_math
方法 B:直接运行脚本
在测试文件末尾添加:
if __name__ == '__main__':
unittest.main()
然后直接运行 python test_my_math.py。
进阶:参数化
标准库的 unittest 不直接支持参数化(即用不同参数运行同一个测试逻辑),通常需要使用第三方库 parameterized。
安装:
pip install parameterized
使用:
import unittest
from parameterized import parameterized
class TestAdd(unittest.TestCase):
@parameterized.expand([
(1, 1, 2),
(2, 3, 5),
(10, 5, 15),
(0, 0, 0),
])
def test_add(self, a, b, expected):
from my_math import add
self.assertEqual(add(a, b), expected)
Mock 对象
在单元测试中,我们经常需要隔离外部依赖(如 API 请求、数据库、时间函数)。unittest.mock (Python 3.3+ 内置) 提供了强大的 Mock 功能。
from unittest.mock import Mock
# 创建一个模拟对象
m = Mock()
# 设置返回值
m.some_method.return_value = 100
# 调用
result = m.some_method("arg1", "arg2")
# 断言
assert result == 100
# 检查是否被调用过
m.some_method.assert_called()
# 检查是否以特定参数被调用
m.some_method.assert_called_with("arg1", "arg2")
- 文件命名:测试文件最好以
test_开头。 - 类命名:测试类继承
unittest.TestCase。 - 方法命名:测试方法必须以
test_开头。 - 断言:使用
self.assertEqual等方法,不要用原生的assert。 - 运行:使用
python -m unittest discover进行批量测试。
