pytest 通过实例讲清单元测试、集成测试、测试覆盖率
1. 单元测试
概念
- 定义: 单元测试是对代码中最小功能单元的测试,通常是函数或类的方法。
- 目标: 验证单个功能是否按照预期工作,而不依赖其他模块或外部资源。
- 特点: 快速、独立,通常是开发者最先编写的测试。
示例:pytest 实现单元测试
# 功能模块:一个简单的数学函数
def add(x, y):"""加法函数"""return x + ydef divide(x, y):"""除法函数,包含除零检查"""if y == 0:raise ValueError("Cannot divide by zero")return x / y# 测试模块:单元测试
def test_add():"""测试 add 函数"""assert add(2, 3) == 5 # 正常情况assert add(-1, 1) == 0 # 边界值assert add(0, 0) == 0 # 特殊情况def test_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 实现集成测试
使用数据库模拟的场景
假设我们有一个用户管理模块,需要测试用户的创建、查询和删除功能:
# 功能模块:用户管理
class UserDatabase:"""模拟用户数据库"""def __init__(self):self.users = {}def add_user(self, username, email):"""添加用户"""if username in self.users:raise ValueError("User already exists")self.users[username] = emaildef get_user(self, username):"""获取用户"""return self.users.get(username)def delete_user(self, username):"""删除用户"""if username in self.users:del self.users[username]else:raise ValueError("User does not exist")# 测试模块:集成测试
def test_user_database():"""测试用户数据库模块的集成功能"""db = UserDatabase()# 添加用户db.add_user("alice", "alice@example.com")assert db.get_user("alice") == "alice@example.com"# 删除用户db.delete_user("alice")assert db.get_user("alice") is None# 测试异常情况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 获取数据
def fetch_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 MagicMockdef test_fetch_data():"""测试 fetch_data 函数,使用 Mock 模拟 API 行为"""# 创建 Mock API 客户端mock_client = MagicMock()# 模拟成功响应mock_client.get.return_value.status_code = 200mock_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: CIon:push:branches:- mainjobs:test:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3- name: Set up Pythonuses: actions/setup-python@v4with:python-version: '3.9'- name: Install dependenciesrun: |python -m pip install --upgrade pippip install pytest pytest-cov codecov- name: Run tests with coveragerun: |pytest --cov=my_project- name: Upload coverage to Codecovuses: codecov/codecov-action@v3with:file: ./coverage.xml
(3)提交后,GitHub Actions 会自动运行测试并上传覆盖率到 Codecov。
4. 添加徽章
在 Codecov 项目的设置中获取徽章链接,将其添加到你的 README.md 文件中,例如:
[](https://codecov.io/gh/<username>/<repo>)
覆盖率目标
-
行业标准:
- 一般项目:60%-80% 及格。
- 关键项目:95%+(例如金融系统、医疗系统)。
-
不能盲目追求100%:覆盖率高不一定代表没有 bug,关注测试的质量比单纯提高覆盖率更重要。
通过这些步骤,你的项目可以在 GitHub 上显示测试覆盖率,并增强项目的专业性和可信度!
相关文章:
pytest 通过实例讲清单元测试、集成测试、测试覆盖率
1. 单元测试 概念 定义: 单元测试是对代码中最小功能单元的测试,通常是函数或类的方法。目标: 验证单个功能是否按照预期工作,而不依赖其他模块或外部资源。特点: 快速、独立,通常是开发者最先编写的测试。 示例:pytest 实现单…...
C#里怎么样自己实现10进制转换为二进制?
C#里怎么样自己实现10进制转换为二进制? 很多情况下,我们都是采用C#里类库来格式化输出二进制数。 如果有人要你自己手写一个10进制数转换为二进制数,并格式化输出, 就可以采用本文里的方法。 这里采用求模和除法来实现的。 下…...
Kafka-Consumer理论知识
一、上下文 之前的博客我们分析了Kafka的设计思想、Kafka的Producer端、Kafka的Server端的分析,为了完整性,我们接下来分析下Kafka的Consumer。《Kafka-代码示例》中有对应的Consumer示例代码,我们以它为入口进行分析 二、KafkaConsumer是什…...
Js-对象-04-Array
重点关注:Array String JSON BOM DOM Array Array对象时用来定义数组的。常用语法格式有如下2种: 方式1: var 变量名 new Array(元素列表); 例如: var arr new Array(1,2,3,4); //1,2,3,4 是存储在数组中的数据࿰…...
React 第八节组件生命周期钩子-类式组件,函数式组件模拟生命周期用法
概述 React组件的生命周期可以分为三个主要阶段: 挂载阶段(Mounting):组件被创建,插入到DOM 树的过程; 更新阶段(Updating):是组件中 props 以及state 发生变化时&#…...
Dubbo源码解析-服务调用(七)
一、服务调用流程 服务在订阅过程中,把notify 过来的urls 都转成了invoker,不知道大家是否还记得前面的rpc 过程,protocol也是在服务端和消费端各连接子一个invoker,如下图: 这张图主要展示rpc 主流程,消费…...
svn 崩溃、 cleanup失败 怎么办
在使用svn的过程中,可能出现整个svn崩溃, 例如cleanup 失败的情况,类似于 这时可以下载本贴资源文件并解压。 或者直接访问网站 SQLite Download Page 进行下载 解压后得到 sqlite3.exe 放到发生问题的svn根目录的.svn路径下 右键呼出pow…...
【Linux系列】NTP时间同步服务器搭建完整指南
在分布式系统和高可用环境中,时间同步是至关重要的。特别是对于银行、金融等关键业务系统,精准的时间同步不仅关系到系统的稳定性,还直接影响交易处理、日志管理、日终结算等功能。本文将介绍NTP(Network Time Protocol࿰…...
go 结构体方法
在 Go 语言中,结构体方法是指附加到结构体类型上的函数。这些方法可以通过结构体的实例来调用。方法的接收者(receiver)指定了该方法属于哪个结构体类型。接收者可以是一个值类型或指针类型。 定义结构体方法 下面是如何为一个结构体定义方…...
DHCP服务(包含配置过程)
目录 一、 DHCP的定义 二、 使用DHCP的好处 三、 DHCP的分配方式 四、 DHCP的租约过程 1. 客户机请求IP 2. 服务器响应 3. 客户机选择IP 4. 服务器确定租约 5. 重新登录 6. 更新租约 五、 DHCP服务配置过程 一、 DHCP的定义 DHCP(Dynamic Host Configur…...
uniapp内嵌的webview H5与应用通信
H5端: 1、找到index.html引入依赖 <script type"text/javascript" src"https://unpkg.com/dcloudio/uni-webview-js0.0.3/index.js"></script> 2、在需要通讯处发送消息 uni.postMessage({data:{code:200,msg:"处理完成&q…...
Android OpenGL ES详解——绘制圆角矩形
1、绘制矩形 代码如下: renderer类: package com.example.roundrectimport android.content.Context import android.opengl.GLES30 import android.opengl.GLSurfaceView.Renderer import com.opengllib.data.VertexArray import com.opengllib.prog…...
网络基础二
文章目录 协议定制,序列化和反序列化应用层网络版计算器协议的定制序列反序列化序列化未复用版 反序列化 TCP是面向字节流的,你怎么保证,你读取上来的数据,是‘’一个“ “完整””的报文呢? 我们没有区分字符串里面有…...
从Full-Text Search全文检索到RAG检索增强
从Full-Text Search全文检索到RAG检索增强 时光飞逝,转眼间六年过去了,六年前铁蛋优化单表千万级数据查询性能的场景依然历历在目,铁蛋也从最开始做CRUD转行去了大数据平台开发,混迹包装开源的业务,机缘巧合下做了实时…...
springMVC 全局异常统一处理
全局异常处理⽅式⼀: 1、配置简单异常处理器 配置 SimpleMappingExceptionResolver 对象: <!-- 配置全局异常统⼀处理的 Bean (简单异常处理器) --> <bean class"org.springframework.web.servlet.handler.SimpleMappingExceptionReso…...
qt ubuntu i386 系统
sudo ln -s cmake-3.31.0-linux-x86_64/bin/* /usr/local/bin 【Ubuntu20.4安装QT6 - CSDN App】Ubuntu20.4安装QT6_ubuntu安装qt6-CSDN博客 sudo ../configure -release -platform linux-g-64 -static -nomake examples -nomake demos -no-qt3support -no-script -no-scriptt…...
BUUCTF—Reverse—helloword(6)
一道安卓逆向的签到题 下载附件 使用JADX-gui反编译工具打开(注意配环境),找到主函数 jadx 本身就是一个开源项目,源代码已经在 Github 上开源了 官方地址:GitHub - skylot/jadx: Dex to Java decompiler 发现flag …...
深入解析下oracle date底层存储方式
之前我们介绍了varchar2和char的数据库底层存储格式,今天我们介绍下date类型的数据存储格式,并通过测试程序快速获取一个日期。 一、环境搭建 1.1,创建表 我们还是创建一个测试表t_code,并插入数据: 1.2,…...
Elasticsearch 开放推理 API 增加了对 IBM watsonx.ai Slate 嵌入模型的支持
作者:来自 Elastic Saikat Sarkar 使用 Elasticsearch 向量数据库构建搜索 AI 体验时如何使用 IBM watsonx™ Slate 文本嵌入。 Elastic 很高兴地宣布,通过集成 IBM watsonx™ Slate 嵌入模型,我们的开放推理 API 功能得以扩展,这…...
如何搭建一个小程序:从零开始的详细指南
在当今数字化时代,小程序以其轻便、无需下载安装即可使用的特点,成为了连接用户与服务的重要桥梁。无论是零售、餐饮、教育还是娱乐行业,小程序都展现了巨大的潜力。如果你正考虑搭建一个小程序,本文将为你提供一个从零开始的详细…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
