当前位置:首页>python>Python unittest框架——官方单元测试框架

Python unittest框架——官方单元测试框架

  • 2026-10-11 08:14:15
Python unittest框架——官方单元测试框架

一、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)
a == b
assertNotEqual(a, b)
a != b
assertTrue(x)
x 为 True
assertFalse(x)
x 为 False
assertIs(a, b)
a is b
assertIsNot(a, b)
a is not b
assertIsNone(x)
x is None
assertIsNotNone(x)
x is not None
assertIn(a, b)
a in b
assertNotIn(a, b)
a not in b
assertIsInstance(a, b)
isinstance(a, b)
assertNotIsInstance(a, b)
not isinstance(a, b)

4.2 数值比较断言

断言方法
说明
assertAlmostEqual(a, b)
round(a-b, 7) == 0
assertNotAlmostEqual(a, b)
round(a-b, 7) != 0
assertGreater(a, b)
a > b
assertGreaterEqual(a, b)
a >= b
assertLess(a, b)
a < b
assertLessEqual(a, b)
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

特性
unittest
pytest
内置
✅
需安装
测试类
必须继承 TestCase
可选
断言
self.assertEqual()assert
固件
setUp
/tearDown
fixture
参数化
需额外库
内置 @pytest.mark.parametrize
插件生态
有限
丰富
学习曲线
平缓
中等

十、最佳实践

  1. 1. 命名规范:测试文件以 test_ 开头,测试方法以 test_ 开头。
  2. 2. 测试独立性:每个测试应该独立,不依赖其他测试。
  3. 3. 使用 setUp/tearDown:减少重复代码,确保测试环境一致。
  4. 4. 一个测试一个断言:单个测试只验证一个行为。
  5. 5. 测试名称清晰:名称应描述被测试的功能。
  6. 6. 测试边界条件:包括正常值、边界值、异常值。
  7. 7. 持续运行测试:集成到 CI/CD 流程中。

十一、总结

核心要点:

  • • 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 等第三方框架。

最新文章

随机文章