🧪 Python单元测试unittest入门与最佳实践:从assert到Mock
<p>在软件开发中,<strong>测试</strong>是确保代码质量的关键环节。Python标准库中的<code>unittest</code>模块为我们提供了强大而灵活的单元测试框架,无需额外安装即可使用。本文将带你从入门到进阶,掌握unittest的核心概念与最佳实践。</p><h2>一、unittest基础概念</h2>
<p>unittest是Python内置的单元测试框架,灵感来源于JUnit。核心概念包括:</p>
<ul>
<li><strong>Test Case(测试用例)</strong>:最小的测试单元,继承自<code>unittest.TestCase</code></li>
<li><strong>Test Suite(测试套件)</strong>:多个测试用例的集合</li>
<li><strong>Test Runner(测试运行器)</strong>:执行测试并输出结果</li>
<li><strong>Fixture(测试夹具)</strong>:测试前的准备和测试后的清理工作</li>
</ul>
<h2>二、编写第一个测试</h2>
<p>创建一个简单的计算器类及其测试:</p>
<pre class="brush:python;toolbar:false"># 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</pre>
<p>对应的测试代码:</p>
<pre class="brush:python;toolbar:false"># 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()</pre>
<h2>三、常用断言方法</h2>
<p>unittest.TestCase提供了丰富的断言方法:</p>
<table>
<tbody>
<tr><th>方法</th><th>用途</th></tr>
<tr>
<td>assertEqual(a, b)</td>
<td>验证a == b</td>
</tr>
<tr>
<td>assertNotEqual(a, b)</td>
<td>验证a != b</td>
</tr>
<tr>
<td>assertTrue(x)</td>
<td>验证x为True</td>
</tr>
<tr>
<td>assertFalse(x)</td>
<td>验证x为False</td>
</tr>
<tr>
<td>assertIs(a, b)</td>
<td>验证a is b</td>
</tr>
<tr>
<td>assertIsNone(x)</td>
<td>验证x is None</td>
</tr>
<tr>
<td>assertIn(a, b)</td>
<td>验证a in b</td>
</tr>
<tr>
<td>assertIsInstance(a, b)</td>
<td>验证isinstance(a, b)</td>
</tr>
<tr>
<td>assertRaises(exc)</td>
<td>验证抛出异常</td>
</tr>
<tr>
<td>assertAlmostEqual(a, b)</td>
<td>验证浮点数近似相等</td>
</tr>
</tbody>
</table>
<h2>四、高级特性</h2>
<h3>1. 跳过测试与预期失败</h3>
<pre class="brush:python;toolbar:false">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)# 已知会失败</pre>
<h3>2. 使用Mock对象</h3>
<pre class="brush:python;toolbar:false">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()</pre>
<h2>五、测试组织与发现</h2>
<p>对于大型项目,合理的测试组织至关重要:</p>
<pre class="brush:bash;toolbar:false">project/
├── src/
│ ├── __init__.py
│ └── calculator.py
└── tests/
├── __init__.py
├── test_calculator.py
└── test_integration.py</pre>
<p>运行测试的多种方式:</p>
<pre class="brush:bash;toolbar:false"># 运行单个测试文件
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</pre>
<h2>六、最佳实践总结</h2>
<ol>
<li><strong>测试命名</strong>:使用<code>test_</code>前缀,描述清楚测试意图</li>
<li><strong>独立性</strong>:每个测试应独立运行,不依赖其他测试</li>
<li><strong>单一职责</strong>:一个测试只验证一个概念</li>
<li><strong>使用setUp/tearDown</strong>:合理管理测试资源</li>
<li><strong>Mock外部依赖</strong>:单元测试应隔离外部系统</li>
<li><strong>覆盖率目标</strong>:核心代码建议达到80%以上覆盖率</li>
</ol>
<p>使用<code>coverage</code>工具检查测试覆盖率:</p>
<pre class="brush:bash;toolbar:false">pip install coverage
coverage run -m unittest discover
coverage report
coverage html# 生成HTML报告</pre>
<h2>总结</h2>
<p>unittest作为Python标准库的测试框架,功能完备且无需额外依赖。掌握其基础用法后,配合Mock和覆盖率工具,可以构建稳健的测试体系。对于更现代的测试需求,也可以考虑pytest,它与unittest完全兼容且语法更简洁。</p>
<h2>参考资料</h2>
<ul>
<li>Python官方文档 - unittest</li>
<li>unittest.mock — 模拟对象库</li>
<li>《Python测试驱动开发》Harry J.W. Percival 著</li>
</ul>
<p>(本文内容由AI生成,仅供学习参考)</p><br><br>
来源:https://www.cnblogs.com/cartech/p/19875645
頁:
[1]