1. 单元测试
概念
- 定义: 单元测试是对代码中最小功能单元的测试,通常是函数或类的方法。
- 目标: 验证单个功能是否按照预期工作,而不依赖其他模块或外部资源。
- 特点: 快速、独立,通常是开发者最先编写的测试。
示例:pytest 实现单元测试
# 功能模块:一个简单的数学函数defadd(x, y):"""
加法函数
"""return x + y
defdivide(x, y):"""
除法函数,包含除零检查
"""if y ==0:raise ValueError("Cannot divide by zero")return x / y
# 测试模块:单元测试deftest_add():"""
测试 add 函数
"""assert add(2,3)==5# 正常情况assert add(-1,1)==0# 边界值assert add(0,0)==0# 特殊情况deftest_divide():"""
测试 divide 函数
"""assert divide(10,2)==5# 正常情况with pytest.raises(ValueError, match="Cannot divide by zero"):
divide(1,0)# 测试除零异常
执行命令
运行单元测试:
pytest test_example.py
优点:
- 快速反馈代码问题。
- 单一功能模块的高覆盖率。
2. 集成测试
概念
- 定义: 集成测试是验证多个模块的交互行为是否正常,确保它们组合在一起能够按预期工作。
- 目标: 检查模块之间的接口和协作行为,可能涉及数据库、API 或文件系统等外部依赖。
- 特点: 比单元测试慢,但更贴近实际场景。
示例:pytest 实现集成测试
使用数据库模拟的场景
假设我们有一个用户管理模块,需要测试用户的创建、查询和删除功能:
# 功能模块:用户管理classUserDatabase:"""
模拟用户数据库
"""def__init__(self):
self.users ={}defadd_user(self, username, email):"""
添加用户
"""if username in self.users:raise ValueError("User already exists")
self.users[username]= email
defget_user(self, username):"""
获取用户
"""return self.users.get(username)defdelete_user(self, username):"""
删除用户
"""if username in self.users:del self.users[username]else:raise ValueError("User does not exist")# 测试模块:集成测试deftest_user_database():"""
测试用户数据库模块的集成功能
"""
db = UserDatabase()# 添加用户
db.add_user("alice","[email protected]")assert db.get_user("alice")=="[email protected]"# 删除用户
db.delete_user("alice")assert db.get_user("alice")isNone# 测试异常情况with pytest.raises(ValueError, match="User does not exist"):
db.delete_user("alice")
执行命令
运行集成测试:
pytest test_example.py
单元测试与集成测试的区别
特性单元测试集成测试****测试范围单一模块或函数多个模块之间的交互目标验证单独功能是否正确验证整体功能是否按预期工作速度快速较慢复杂度较低较高,可能涉及外部依赖测试工具模拟对象 (Mock)实际环境或部分模拟环境
3. pytest 中的 Mock 模拟(用于集成测试中的外部依赖)
在集成测试中,我们可能需要模拟外部依赖(如数据库、API)。
pytest
支持使用
unittest.mock
来实现 Mock。
示例:模拟外部 API
假设我们有一个函数需要从外部 API 获取数据:
# 功能模块:从外部 API 获取数据deffetch_data(api_client):"""
从外部 API 客户端获取数据
"""
response = api_client.get("/data")if response.status_code ==200:return response.json()else:raise ValueError("Failed to fetch data")
测试:使用 Mock 模拟 API
from unittest.mock import MagicMock
deftest_fetch_data():"""
测试 fetch_data 函数,使用 Mock 模拟 API 行为
"""# 创建 Mock API 客户端
mock_client = MagicMock()# 模拟成功响应
mock_client.get.return_value.status_code =200
mock_client.get.return_value.json.return_value ={"key":"value"}# 调用函数并验证返回值
result = fetch_data(mock_client)assert result =={"key":"value"}# 验证 API 是否被正确调用
mock_client.get.assert_called_once_with("/data")
运行测试
使用以下命令运行测试:
pytest test_example.py
4. 测试组合:单元测试 + 集成测试
实际开发中,建议结合单元测试和集成测试:
- 单元测试:覆盖每个功能单元,确保模块内部逻辑正确。
- 集成测试:验证模块之间的交互和整体功能。
最佳实践
- 单元测试优先: 先确保每个功能单元稳定。
- 集成测试补充: 验证整体流程时,再引入集成测试。
- Mock 外部依赖: 在集成测试中尽量减少对真实资源(数据库、网络)的依赖。
什么是项目的测试覆盖率?
测试覆盖率(Test Coverage)是衡量一个项目中有多少代码被测试用例覆盖的指标。它表示项目代码的质量保证程度。测试覆盖率通常以百分比的形式表示,如 80% 表示代码中 80% 的部分已经被测试用例运行过。
覆盖率分类
- 行覆盖率(Line Coverage) 检测每一行代码是否被执行。
- 分支覆盖率(Branch Coverage) 检测代码中的条件语句(如 if-else)的所有分支是否都被测试。
- 函数覆盖率(Function Coverage) 检测所有函数是否被调用。
- 路径覆盖率(Path Coverage) 检测所有可能的执行路径是否都被测试。
为什么测试覆盖率重要?
- 质量保证:确保关键代码路径经过充分测试。
- 维护性:发现未被测试的代码,优化测试用例。
- 团队规范:强制要求开发者在提交代码前编写测试。
如何计算测试覆盖率?
工具
在 Python 项目中,通常使用以下工具计算测试覆盖率:
- pytest-cov:配合
pytest
使用,易于集成。 - Coverage.py:独立的覆盖率工具,可生成详细的覆盖率报告。
- Codecov 和 Coveralls:托管服务,用于在 GitHub 等平台展示测试覆盖率。
在 GitHub 上展示测试覆盖率
许多开源项目在 GitHub 上会显示覆盖率指标,通过徽章(Badge)的形式展示,通常借助 Codecov 或 Coveralls 服务实现。
如何在 GitHub 项目中添加测试覆盖率?
1. 安装依赖
确保已安装以下工具:
pip install pytest pytest-cov
pip install codecov
2. 配置 pytest-cov
在项目中运行测试并生成覆盖率报告:
pytest --cov=my_project --cov-report=xml
这将生成一个
coverage.xml
文件,供上传到 Codecov 或其他服务。
3. 集成 Codecov
(1)登录 Codecov 并连接你的 GitHub 项目。
(2)在项目根目录添加一个
.github/workflows/codecov.yml
文件:
name: CI
on:push:branches:- main
jobs:test:runs-on: ubuntu-latest
steps:-uses: actions/checkout@v3
-name: Set up Python
uses: actions/setup-python@v4
with:python-version:'3.9'-name: Install dependencies
run:|
python -m pip install --upgrade pip
pip install pytest pytest-cov codecov-name: Run tests with coverage
run:|
pytest --cov=my_project-name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:file: ./coverage.xml
(3)提交后,GitHub Actions 会自动运行测试并上传覆盖率到 Codecov。
4. 添加徽章
在 Codecov 项目的设置中获取徽章链接,将其添加到你的
README.md
文件中,例如:
[![codecov](https://codecov.io/gh/<username>/<repo>/branch/main/graph/badge.svg)](https://codecov.io/gh/<username>/<repo>)
覆盖率目标
- 行业标准:- 一般项目:60%-80% 及格。- 关键项目:95%+(例如金融系统、医疗系统)。
- 不能盲目追求100%:覆盖率高不一定代表没有 bug,关注测试的质量比单纯提高覆盖率更重要。
通过这些步骤,你的项目可以在 GitHub 上显示测试覆盖率,并增强项目的专业性和可信度!
版权归原作者 Coding Is Fun 所有, 如有侵权,请联系我们删除。