🧪 Python单元测试unittest入门与最佳实践:从assert到Mock
在软件开发中,测试是确保代码质量的关键环节。Python标准库中的unittest模块为我们提供了强大而灵活的单元测试框架,无需额外安装即可使用。本文将带你从入门到进阶,掌握unittest的核心概念与最佳实践。
一、unittest基础概念
unittest是Python内置的单元测试框架,灵感来源于JUnit。核心概念包括:
- Test Case(测试用例):最小的测试单元,继承自
unittest.TestCase - Test Suite(测试套件):多个测试用例的集合
- Test Runner(测试运行器):执行测试并输出结果
- Fixture(测试夹具):测试前的准备和测试后的清理工作
二、编写第一个测试
创建一个简单的计算器类及其测试:
# calculator.py
class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
def multiply(self, a, b):
return a * b
def divide(self, a, b):
if b == 0:
raise ValueError("除数不能为零")
return a / b
对应的测试代码:
# test_calculator.py
import unittest
from calculator import Calculator
class TestCalculator(unittest.TestCase):
def setUp(self):
# 每个测试方法前执行
self.calc = Calculator()
def tearDown(self):
# 每个测试方法后执行
pass
def test_add(self):
self.assertEqual(self.calc.add(2, 3), 5)
self.assertEqual(self.calc.add(-1, 1), 0)
def test_subtract(self):
self.assertEqual(self.calc.subtract(10, 5), 5)
self.assertEqual(self.calc.subtract(-1, -1), 0)
def test_multiply(self):
self.assertEqual(self.calc.multiply(3, 4), 12)
self.assertEqual(self.calc.multiply(-2, 3), -6)
def test_divide(self):
self.assertEqual(self.calc.divide(10, 2), 5)
self.assertAlmostEqual(self.calc.divide(7, 2), 3.5)
def test_divide_by_zero(self):
with self.assertRaises(ValueError):
self.calc.divide(10, 0)
if __name__ == '__main__':
unittest.main()
三、常用断言方法
unittest.TestCase提供了丰富的断言方法:
| 方法 | 用途 |
|---|---|
| assertEqual(a, b) | 验证a == b |
| assertNotEqual(a, b) | 验证a != b |
| assertTrue(x) | 验证x为True |
| assertFalse(x) | 验证x为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(exc) | 验证抛出异常 |
| assertAlmostEqual(a, b) | 验证浮点数近似相等 |
四、高级特性
1. 跳过测试与预期失败
import unittest
import sys
class TestAdvanced(unittest.TestCase):
@unittest.skip("暂时跳过此测试")
def test_skip(self):
pass
@unittest.skipIf(sys.platform == 'win32', 'Windows平台跳过')
def test_skip_windows(self):
pass
@unittest.expectedFailure
def test_expected_failure(self):
self.assertEqual(1, 2) # 已知会失败
2. 使用Mock对象
from unittest.mock import Mock, patch, MagicMock
# 创建Mock对象
mock = Mock()
mock.return_value = 42
print(mock()) # 输出: 42
# 使用patch装饰器
import requests
class TestAPI(unittest.TestCase):
@patch('requests.get')
def test_fetch_data(self, mock_get):
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {'data': 'test'}
result = requests.get('https://api.example.com')
self.assertEqual(result.status_code, 200)
mock_get.assert_called_once()
五、测试组织与发现
对于大型项目,合理的测试组织至关重要:
project/
├── src/
│ ├── __init__.py
│ └── calculator.py
└── tests/
├── __init__.py
├── test_calculator.py
└── test_integration.py
运行测试的多种方式:
# 运行单个测试文件 python -m unittest test_calculator # 运行具体测试类 python -m unittest test_calculator.TestCalculator # 运行具体测试方法 python -m unittest test_calculator.TestCalculator.test_add # 自动发现并运行所有测试 python -m unittest discover -s tests -v # 生成HTML测试报告(需安装html-testRunner) pip install html-testRunner
六、最佳实践总结
- 测试命名:使用
test_前缀,描述清楚测试意图 - 独立性:每个测试应独立运行,不依赖其他测试
- 单一职责:一个测试只验证一个概念
- 使用setUp/tearDown:合理管理测试资源
- Mock外部依赖:单元测试应隔离外部系统
- 覆盖率目标:核心代码建议达到80%以上覆盖率
使用coverage工具检查测试覆盖率:
pip install coverage coverage run -m unittest discover coverage report coverage html # 生成HTML报告
总结
unittest作为Python标准库的测试框架,功能完备且无需额外依赖。掌握其基础用法后,配合Mock和覆盖率工具,可以构建稳健的测试体系。对于更现代的测试需求,也可以考虑pytest,它与unittest完全兼容且语法更简洁。
参考资料
- Python官方文档 - unittest
- unittest.mock — 模拟对象库
- 《Python测试驱动开发》Harry J.W. Percival 著
(本文内容由AI生成,仅供学习参考)

浙公网安备 33010602011771号