一、unittest概述
unittest 是 Python 标准库自带的单元测试框架,灵感来源于 Java 的 JUnit。它提供了测试用例组织、测试固件、断言方法、测试运行器等完整功能,是 Python 官方推荐的测试工具。
import unittest
classTestMath(unittest.TestCase):
deftest_add(self):
self.assertEqual(2 + 3, 5)
deftest_subtract(self):
self.assertEqual(5 - 3, 2)
if __name__ == '__main__':
unittest.main()
二、unittest核心组件
| |
| TestCase | |
| TestSuite | |
| TestRunner | |
| TestLoader | |
| TestFixture | |
三、基本用法
3.1 创建测试用例
import unittest
classTestStringMethods(unittest.TestCase):
deftest_upper(self):
self.assertEqual('hello'.upper(), 'HELLO')
deftest_isupper(self):
self.assertTrue('HELLO'.isupper())
self.assertFalse('Hello'.isupper())
deftest_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
withself.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
3.2 运行测试
# 运行单个文件
python test_math.py
# 详细输出(-v)
python test_math.py -v
# 运行指定测试类
python test_math.py TestMath
# 运行指定测试方法
python test_math.py TestMath.test_add
四、断言方法
4.1 常用断言
| |
assertEqual(a, b) | |
assertNotEqual(a, b) | |
assertTrue(x) | |
assertFalse(x) | |
assertIs(a, b) | |
assertIsNot(a, b) | |
assertIsNone(x) | |
assertIsNotNone(x) | |
assertIn(a, b) | |
assertNotIn(a, b) | |
assertIsInstance(a, b) | |
assertNotIsInstance(a, b) | |
4.2 数值比较断言
| |
assertAlmostEqual(a, b) | |
assertNotAlmostEqual(a, b) | |
assertGreater(a, b) | |
assertGreaterEqual(a, b) | |
assertLess(a, b) | |
assertLessEqual(a, b) | |
4.3 异常断言
import unittest
defdivide(a, b):
if b == 0:
raise ValueError("除数不能为零")
return a / b
classTestDivide(unittest.TestCase):
deftest_divide_by_zero(self):
# 方式1:使用 assertRaises 上下文管理器
withself.assertRaises(ValueError):
divide(10, 0)
# 方式2:捕获异常对象
withself.assertRaises(ValueError) as cm:
divide(10, 0)
self.assertEqual(str(cm.exception), "除数不能为零")
deftest_divide_normal(self):
result = divide(10, 2)
self.assertEqual(result, 5)
4.4 集合断言
classTestCollections(unittest.TestCase):
deftest_list_equality(self):
self.assertListEqual([1, 2, 3], [1, 2, 3])
deftest_tuple_equality(self):
self.assertTupleEqual((1, 2), (1, 2))
deftest_dict_equality(self):
self.assertDictEqual({'a': 1, 'b': 2}, {'b': 2, 'a': 1})
deftest_set_equality(self):
self.assertSetEqual({1, 2, 3}, {3, 2, 1})
4.5 自定义错误消息
deftest_with_message(self):
value = 10
self.assertEqual(value, 5, f"预期是5,实际是{value}")
五、测试固件(Fixtures)
5.1 方法级别
import unittest
classTestDatabase(unittest.TestCase):
defsetUp(self):
"""每个测试方法执行前运行"""
print("准备测试数据")
self.data = [1, 2, 3]
deftearDown(self):
"""每个测试方法执行后运行"""
print("清理测试数据")
self.data = None
deftest_something(self):
self.assertEqual(len(self.data), 3)
5.2 类级别
classTestDatabase(unittest.TestCase):
@classmethod
defsetUpClass(cls):
"""整个测试类执行前运行一次"""
print("连接数据库")
cls.connection = "database_connection"
@classmethod
deftearDownClass(cls):
"""整个测试类执行后运行一次"""
print("关闭数据库连接")
cls.connection = None
deftest_query(self):
self.assertIsNotNone(self.connection)
5.3 模块级别
import unittest
defsetUpModule():
"""模块内所有测试执行前运行"""
print("模块初始化")
deftearDownModule():
"""模块内所有测试执行后运行"""
print("模块清理")
classTestModule1(unittest.TestCase):
deftest_a(self):
self.assertTrue(True)
六、测试套件
6.1 手动构建套件
import unittest
from test_math import TestMath
from test_string import TestString
defsuite():
suite = unittest.TestSuite()
suite.addTest(TestMath('test_add'))
suite.addTest(TestMath('test_subtract'))
suite.addTest(TestString('test_upper'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
6.2 自动发现测试
# 自动发现当前目录下所有 test_*.py 文件
suite = unittest.defaultTestLoader.discover('tests', pattern='test_*.py')
unittest.TextTestRunner().run(suite)
七、跳过测试
import unittest
import sys
classTestSkip(unittest.TestCase):
deftest_not_implemented(self):
self.skipTest("功能未实现")
@unittest.skip("跳过此测试")
deftest_skip_demo(self):
self.assertTrue(False)
@unittest.skipIf(sys.version_info < (3, 8), "需要 Python 3.8+")
deftest_python_version(self):
self.assertTrue(True)
@unittest.skipUnless(sys.platform.startswith("linux"), "仅 Linux 平台")
deftest_linux_only(self):
self.assertTrue(True)
@unittest.expectedFailure
deftest_expected_failure(self):
self.assertEqual(1, 2) # 预计会失败,不计入失败数
八、实战案例
8.1 测试计算器类
import unittest
classCalculator:
defadd(self, a, b):
return a + b
defsubtract(self, a, b):
return a - b
defmultiply(self, a, b):
return a * b
defdivide(self, a, b):
if b == 0:
raise ValueError("除数不能为零")
return a / b
classTestCalculator(unittest.TestCase):
@classmethod
defsetUpClass(cls):
print("初始化计算器测试")
cls.calc = Calculator()
@classmethod
deftearDownClass(cls):
print("计算器测试完成")
cls.calc = None
defsetUp(self):
print(" 执行测试前准备")
deftearDown(self):
print(" 执行测试后清理")
deftest_add_positive(self):
self.assertEqual(self.calc.add(2, 3), 5)
deftest_add_negative(self):
self.assertEqual(self.calc.add(-1, -2), -3)
deftest_add_zero(self):
self.assertEqual(self.calc.add(5, 0), 5)
deftest_subtract(self):
self.assertEqual(self.calc.subtract(10, 3), 7)
self.assertEqual(self.calc.subtract(5, 8), -3)
deftest_multiply(self):
self.assertEqual(self.calc.multiply(4, 5), 20)
self.assertEqual(self.calc.multiply(-3, 2), -6)
deftest_divide_normal(self):
self.assertEqual(self.calc.divide(10, 2), 5)
self.assertAlmostEqual(self.calc.divide(10, 3), 3.3333333333333335)
deftest_divide_by_zero(self):
withself.assertRaises(ValueError):
self.calc.divide(10, 0)
deftest_add_floats(self):
result = self.calc.add(0.1, 0.2)
self.assertAlmostEqual(result, 0.3, places=7)
if __name__ == '__main__':
unittest.main()
8.2 测试用户管理系统
import unittest
classUser:
def__init__(self, username, email):
self.username = username
self.email = email
self.is_active = True
defdeactivate(self):
self.is_active = False
classUserManager:
def__init__(self):
self.users = []
defadd_user(self, username, email):
ifself.find_user_by_username(username):
raise ValueError(f"用户名 {username} 已存在")
user = User(username, email)
self.users.append(user)
return user
deffind_user_by_username(self, username):
for user inself.users:
if user.username == username:
return user
returnNone
defget_active_users(self):
return [u for u inself.users if u.is_active]
defdeactivate_user(self, username):
user = self.find_user_by_username(username)
if user:
user.deactivate()
returnTrue
returnFalse
classTestUserManager(unittest.TestCase):
defsetUp(self):
"""每个测试前创建新的 UserManager"""
self.manager = UserManager()
deftearDown(self):
"""每个测试后清理"""
self.manager = None
deftest_add_user_success(self):
user = self.manager.add_user("john", "john@example.com")
self.assertIsNotNone(user)
self.assertEqual(user.username, "john")
self.assertEqual(user.email, "john@example.com")
self.assertEqual(len(self.manager.users), 1)
deftest_add_user_duplicate(self):
self.manager.add_user("john", "john@example.com")
withself.assertRaises(ValueError) as cm:
self.manager.add_user("john", "john2@example.com")
self.assertIn("已存在", str(cm.exception))
deftest_find_user_existing(self):
self.manager.add_user("jane", "jane@example.com")
user = self.manager.find_user_by_username("jane")
self.assertIsNotNone(user)
self.assertEqual(user.username, "jane")
deftest_find_user_not_existing(self):
user = self.manager.find_user_by_username("nonexistent")
self.assertIsNone(user)
deftest_deactivate_user(self):
self.manager.add_user("tom", "tom@example.com")
result = self.manager.deactivate_user("tom")
self.assertTrue(result)
user = self.manager.find_user_by_username("tom")
self.assertFalse(user.is_active)
deftest_deactivate_user_not_existing(self):
result = self.manager.deactivate_user("nonexistent")
self.assertFalse(result)
deftest_get_active_users(self):
self.manager.add_user("u1", "u1@test.com")
self.manager.add_user("u2", "u2@test.com")
self.manager.deactivate_user("u2")
active = self.manager.get_active_users()
self.assertEqual(len(active), 1)
self.assertEqual(active[0].username, "u1")
if __name__ == '__main__':
unittest.main()
九、unittest vs pytest
| | |
| | |
| | |
| self.assertEqual() | assert |
| setUp | fixture |
| | 内置 @pytest.mark.parametrize |
| | |
| | |
十、最佳实践
- 1. 命名规范:测试文件以
test_ 开头,测试方法以 test_ 开头。 - 2. 测试独立性:每个测试应该独立,不依赖其他测试。
- 3. 使用
setUp/tearDown:减少重复代码,确保测试环境一致。
十一、总结
核心要点:
- •
unittest 是 Python 标准库单元测试框架,无需额外安装。 - • 测试类继承
unittest.TestCase,测试方法以 test_ 开头。 - • 提供丰富的断言方法(
assertEqual, assertTrue, assertRaises 等)。 - • 支持测试固件(
setUp, tearDown, setUpClass, tearDownClass)。 - • 支持测试套件(
TestSuite)和自动发现(TestLoader.discover)。 - • 支持跳过测试(
skip, skipIf, skipUnless)。
运行命令:
python -m unittest test_module.py
python -m unittest discover -s tests -p "test_*.py"
掌握 unittest 是 Python 开发者的必备技能,它为代码质量提供了有力保障。对于更复杂的测试需求,可以考虑 pytest 等第三方框架。