unittest
是 Python 自带的单元测试框架,用于编写和运行可重复的测试用例。它的核心思想是通过断言(assertions)验证代码的行为是否符合预期。以下是 unittest
的基本使用方法:
1. 基本结构
1.1 创建测试类
- 继承
unittest.TestCase
,每个测试用例对应一个方法。 - 测试方法必须 以
test_
开头,否则不会被自动识别为测试用例。
python
import unittest
class TestMathOperations(unittest.TestCase):
def test_addition(self):
self.assertEqual(1 + 1, 2) # 断言 1+1=2
1.2 前置与后置方法
setUp()
: 在每个测试方法执行前运行(如初始化资源)。tearDown()
: 在每个测试方法执行后运行(如清理资源)。setUpClass()
/tearDownClass()
: 在整个测试类的开始/结束时运行(需用@classmethod
修饰)。
python
class TestExample(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("整个测试类开始前执行")
def setUp(self):
print("每个测试方法开始前执行")
def test_example(self):
self.assertTrue(True)
def tearDown(self):
print("每个测试方法结束后执行")
@classmethod
def tearDownClass(cls):
print("整个测试类结束后执行")
2. 断言方法
unittest
提供了丰富的断言方法,常用如下:
方法 | 说明 |
---|---|
assertEqual(a, b) |
检查 a == b |
assertTrue(x) |
检查 x 为 True |
assertFalse(x) |
检查 x 为 False |
assertRaises(Error, func, *args) |
检查函数 func 是否抛出 Error 异常 |
assertIn(a, b) |
检查 a 在 b 中 |
assertIsNone(x) |
检查 x 是 None |
python
def test_assertions(self):
self.assertEqual(3 * 3, 9)
self.assertIn(2, [1, 2, 3])
with self.assertRaises(ZeroDivisionError):
_ = 1 / 0
3. 运行测试
3.1 通过代码运行
在脚本末尾添加:
python
if __name__ == "__main__":
unittest.main()
3.2 通过命令行运行
bash
# 运行单个测试模块
python -m unittest test_module.py
# 自动发现并运行所有测试(推荐)
python -m unittest discover
3.3 指定运行特定测试
bash
# 运行单个测试类
python -m unittest test_module.TestClass
# 运行单个测试方法
python -m unittest test_module.TestClass.test_method
4. 测试套件(Test Suite)
手动组织多个测试用例:
python
suite = unittest.TestSuite()
suite.addTest(TestMathOperations("test_addition"))
suite.addTest(TestExample("test_example"))
runner = unittest.TextTestRunner()
runner.run(suite)
5. 高级用法
5.1 跳过测试
使用装饰器跳过某些测试:
python
@unittest.skip("跳过原因")
def test_skipped(self):
self.fail("不会执行")
@unittest.skipIf(condition, "条件满足时跳过")
def test_conditional_skip(self):
pass
5.2 参数化测试
unittest
本身不支持参数化,但可通过第三方库(如 parameterized
)实现:
python
from parameterized import parameterized
class TestParameterized(unittest.TestCase):
@parameterized.expand([
(2, 3, 5),
(0, 0, 0),
])
def test_add(self, a, b, expected):
self.assertEqual(a + b, expected)
5.3 Mock 对象
使用 unittest.mock
模拟外部依赖:
python
from unittest.mock import Mock
def test_mock(self):
mock_obj = Mock(return_value=42)
self.assertEqual(mock_obj(), 42)
6. 示例项目结构
project/
├── my_code.py # 被测试的代码
└── tests/
├── __init__.py
└── test_code.py # 测试代码
总结
unittest
是 Python 测试的基石,适合中小型项目。对于复杂场景,可以结合第三方库(如 pytest
)增强功能。核心步骤:
- 继承
TestCase
编写测试类。 - 使用
test_
前缀定义测试方法。 - 通过断言验证逻辑。
- 利用
setUp()
/tearDown()
管理资源。 - 运行测试并分析结果。
pytest
是 Python 中最流行的第三方测试框架,以其简洁的语法、强大的功能和灵活的扩展性著称。相比 unittest
,pytest
更注重代码的可读性和可维护性,同时支持丰富的插件生态系统。以下是 pytest
的核心使用方法:
1. 安装 pytest
bash
pip install pytest
2. 基本用法
2.1 编写测试函数
- 测试函数名需以
test_
开头(或_test
结尾)。 - 断言直接使用 Python 原生
assert
语句,无需记忆特定断言方法。
python
# test_sample.py
def test_addition():
assert 1 + 1 == 2
def test_list_contains():
numbers = [1, 2, 3]
assert 2 in numbers
2.2 运行测试
bash
# 运行当前目录所有测试
pytest
# 运行指定文件
pytest test_sample.py
# 运行指定函数
pytest test_sample.py::test_addition
# 显示详细输出(-v 显示用例名称,-s 打印输出)
pytest -v -s
3. 断言增强
pytest
的断言失败信息更直观,能自动展示上下文差异(如列表、字典比较):
python
def test_failure_example():
expected = {"a": 1, "b": 2}
actual = {"a": 1, "b": 3}
assert expected == actual
运行后输出:
AssertionError: assert {'a': 1, 'b': 2} == {'a': 1, 'b': 3}
Differing items:
{'b': 2} != {'b': 3}
4. Fixture(依赖注入)
pytest
的 fixture
机制用于管理测试的依赖资源(如数据库连接、临时文件),支持复用和共享。
4.1 定义 Fixture
python
import pytest
@pytest.fixture
def database_connection():
conn = create_db_connection() # 初始化资源
yield conn # 返回资源
conn.close() # 清理资源
4.2 使用 Fixture
在测试函数中通过参数名直接调用:
python
def test_query(database_connection):
result = database_connection.query("SELECT * FROM users")
assert len(result) > 0
4.3 Fixture 作用域
通过 scope
参数控制生命周期:
python
@pytest.fixture(scope="module") # 作用域:模块级(每个模块执行一次)
def shared_resource():
return initialize_resource()
5. 参数化测试
使用 @pytest.mark.parametrize
对单条测试用例注入多组参数,避免重复代码。
python
import pytest
@pytest.mark.parametrize("a, b, expected", [
(2, 3, 5),
(0, 0, 0),
(-1, 5, 4),
])
def test_add(a, b, expected):
assert a + b == expected
6. 测试异常
使用 pytest.raises
捕获并验证异常:
python
def test_division_by_zero():
with pytest.raises(ZeroDivisionError):
_ = 1 / 0
7. Mock 对象(依赖隔离)
使用 pytest-mock
插件(基于 unittest.mock
)模拟外部依赖:
bash
pip install pytest-mock
示例:
python
def test_mocking(mocker):
mock_requests = mocker.patch("requests.get") # 模拟 requests.get
mock_requests.return_value.status_code = 200
response = requests.get("https://api.example.com")
assert response.status_code == 200
8. 插件扩展
pytest
支持丰富的插件,例如:
pytest-cov
: 测试覆盖率统计pytest-xdist
: 并行运行测试pytest-django
: Django 项目集成pytest-asyncio
: 异步测试支持
安装插件:
bash
pip install pytest-cov pytest-xdist
9. 项目结构
project/
├── src/ # 源代码
│ └── my_module.py
└── tests/ # 测试代码
├── __init__.py
├── conftest.py # 全局 Fixture 定义
├── test_core.py
└── test_api.py
10. 与 unittest 兼容
pytest
可以直接运行 unittest
风格的测试用例:
python
# test_unittest_style.py
import unittest
class TestOldCode(unittest.TestCase):
def test_legacy(self):
self.assertEqual(1 + 1, 2)
运行:
bash
pytest test_unittest_style.py
11. 高级功能
-
标记(Markers) :
用
@pytest.mark
对测试分类(如跳过、标记为慢测试):python@pytest.mark.skip(reason="尚未实现") def test_unimplemented(): assert False @pytest.mark.slow def test_long_running(): # 耗时操作 pass
运行指定标记的测试:
bashpytest -m slow # 只运行标记为 slow 的测试 pytest -m "not slow" # 排除 slow 测试
-
Hook 函数 :
自定义
pytest
行为(如修改报告输出)。
总结
pytest
的优势:
- 简洁性 :使用原生
assert
,减少样板代码。 - 灵活性:Fixture 机制优雅管理测试依赖。
- 扩展性:通过插件支持复杂场景(如异步、分布式测试)。
- 兼容性 :无缝运行
unittest
和nose
测试。
适合从简单脚本到大型项目的全场景测试需求。