当前位置: 首页 > news >正文

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. 测试组合:单元测试 + 集成测试

实际开发中,建议结合单元测试和集成测试:

  1. 单元测试:覆盖每个功能单元,确保模块内部逻辑正确。
  2. 集成测试:验证模块之间的交互和整体功能。

最佳实践

  • 单元测试优先: 先确保每个功能单元稳定。
  • 集成测试补充: 验证整体流程时,再引入集成测试。
  • Mock 外部依赖: 在集成测试中尽量减少对真实资源(数据库、网络)的依赖。

什么是项目的测试覆盖率?

测试覆盖率(Test Coverage)是衡量一个项目中有多少代码被测试用例覆盖的指标。它表示项目代码的质量保证程度。测试覆盖率通常以百分比的形式表示,如 80% 表示代码中 80% 的部分已经被测试用例运行过。

覆盖率分类
  1. 行覆盖率(Line Coverage)
    检测每一行代码是否被执行。

  2. 分支覆盖率(Branch Coverage)
    检测代码中的条件语句(如 if-else)的所有分支是否都被测试。

  3. 函数覆盖率(Function Coverage)
    检测所有函数是否被调用。

  4. 路径覆盖率(Path Coverage)
    检测所有可能的执行路径是否都被测试。

为什么测试覆盖率重要?
  1. 质量保证:确保关键代码路径经过充分测试。
  2. 维护性:发现未被测试的代码,优化测试用例。
  3. 团队规范:强制要求开发者在提交代码前编写测试。

如何计算测试覆盖率?

工具

在 Python 项目中,通常使用以下工具计算测试覆盖率:

  1. pytest-cov:配合 pytest 使用,易于集成。
  2. Coverage.py:独立的覆盖率工具,可生成详细的覆盖率报告。
  3. CodecovCoveralls:托管服务,用于在 GitHub 等平台展示测试覆盖率。

在 GitHub 上展示测试覆盖率

许多开源项目在 GitHub 上会显示覆盖率指标,通过徽章(Badge)的形式展示,通常借助 CodecovCoveralls 服务实现。

如何在 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 文件中,例如:

[![codecov](https://codecov.io/gh/<username>/<repo>/branch/main/graph/badge.svg)](https://codecov.io/gh/<username>/<repo>)

覆盖率目标

  1. 行业标准

    • 一般项目:60%-80% 及格。
    • 关键项目:95%+(例如金融系统、医疗系统)。
  2. 不能盲目追求100%:覆盖率高不一定代表没有 bug,关注测试的质量比单纯提高覆盖率更重要。

通过这些步骤,你的项目可以在 GitHub 上显示测试覆盖率,并增强项目的专业性和可信度!

相关文章:

pytest 通过实例讲清单元测试、集成测试、测试覆盖率

1. 单元测试 概念 定义: 单元测试是对代码中最小功能单元的测试&#xff0c;通常是函数或类的方法。目标: 验证单个功能是否按照预期工作&#xff0c;而不依赖其他模块或外部资源。特点: 快速、独立&#xff0c;通常是开发者最先编写的测试。 示例&#xff1a;pytest 实现单…...

C#里怎么样自己实现10进制转换为二进制?

C#里怎么样自己实现10进制转换为二进制&#xff1f; 很多情况下&#xff0c;我们都是采用C#里类库来格式化输出二进制数。 如果有人要你自己手写一个10进制数转换为二进制数&#xff0c;并格式化输出&#xff0c; 就可以采用本文里的方法。 这里采用求模和除法来实现的。 下…...

Kafka-Consumer理论知识

一、上下文 之前的博客我们分析了Kafka的设计思想、Kafka的Producer端、Kafka的Server端的分析&#xff0c;为了完整性&#xff0c;我们接下来分析下Kafka的Consumer。《Kafka-代码示例》中有对应的Consumer示例代码&#xff0c;我们以它为入口进行分析 二、KafkaConsumer是什…...

Js-对象-04-Array

重点关注&#xff1a;Array String JSON BOM DOM Array Array对象时用来定义数组的。常用语法格式有如下2种&#xff1a; 方式1&#xff1a; var 变量名 new Array(元素列表); 例如&#xff1a; var arr new Array(1,2,3,4); //1,2,3,4 是存储在数组中的数据&#xff0…...

React 第八节组件生命周期钩子-类式组件,函数式组件模拟生命周期用法

概述 React组件的生命周期可以分为三个主要阶段&#xff1a; 挂载阶段&#xff08;Mounting&#xff09;&#xff1a;组件被创建&#xff0c;插入到DOM 树的过程&#xff1b; 更新阶段&#xff08;Updating&#xff09;&#xff1a;是组件中 props 以及state 发生变化时&#…...

Dubbo源码解析-服务调用(七)

一、服务调用流程 服务在订阅过程中&#xff0c;把notify 过来的urls 都转成了invoker&#xff0c;不知道大家是否还记得前面的rpc 过程&#xff0c;protocol也是在服务端和消费端各连接子一个invoker&#xff0c;如下图&#xff1a; 这张图主要展示rpc 主流程&#xff0c;消费…...

svn 崩溃、 cleanup失败 怎么办

在使用svn的过程中&#xff0c;可能出现整个svn崩溃&#xff0c; 例如cleanup 失败的情况&#xff0c;类似于 这时可以下载本贴资源文件并解压。 或者直接访问网站 SQLite Download Page 进行下载 解压后得到 sqlite3.exe 放到发生问题的svn根目录的.svn路径下 右键呼出pow…...

【Linux系列】NTP时间同步服务器搭建完整指南

在分布式系统和高可用环境中&#xff0c;时间同步是至关重要的。特别是对于银行、金融等关键业务系统&#xff0c;精准的时间同步不仅关系到系统的稳定性&#xff0c;还直接影响交易处理、日志管理、日终结算等功能。本文将介绍NTP&#xff08;Network Time Protocol&#xff0…...

go 结构体方法

在 Go 语言中&#xff0c;结构体方法是指附加到结构体类型上的函数。这些方法可以通过结构体的实例来调用。方法的接收者&#xff08;receiver&#xff09;指定了该方法属于哪个结构体类型。接收者可以是一个值类型或指针类型。 定义结构体方法 下面是如何为一个结构体定义方…...

DHCP服务(包含配置过程)

目录 一、 DHCP的定义 二、 使用DHCP的好处 三、 DHCP的分配方式 四、 DHCP的租约过程 1. 客户机请求IP 2. 服务器响应 3. 客户机选择IP 4. 服务器确定租约 5. 重新登录 6. 更新租约 五、 DHCP服务配置过程 一、 DHCP的定义 DHCP&#xff08;Dynamic Host Configur…...

uniapp内嵌的webview H5与应用通信

H5端&#xff1a; 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、绘制矩形 代码如下&#xff1a; renderer类&#xff1a; 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…...

网络基础二

文章目录 协议定制&#xff0c;序列化和反序列化应用层网络版计算器协议的定制序列反序列化序列化未复用版 反序列化 TCP是面向字节流的&#xff0c;你怎么保证&#xff0c;你读取上来的数据&#xff0c;是‘’一个“ “完整””的报文呢&#xff1f; 我们没有区分字符串里面有…...

从Full-Text Search全文检索到RAG检索增强

从Full-Text Search全文检索到RAG检索增强 时光飞逝&#xff0c;转眼间六年过去了&#xff0c;六年前铁蛋优化单表千万级数据查询性能的场景依然历历在目&#xff0c;铁蛋也从最开始做CRUD转行去了大数据平台开发&#xff0c;混迹包装开源的业务&#xff0c;机缘巧合下做了实时…...

springMVC 全局异常统一处理

全局异常处理⽅式⼀: 1、配置简单异常处理器 配置 SimpleMappingExceptionResolver 对象: <!-- 配置全局异常统⼀处理的 Bean &#xff08;简单异常处理器&#xff09; --> <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反编译工具打开&#xff08;注意配环境&#xff09;&#xff0c;找到主函数 jadx 本身就是一个开源项目&#xff0c;源代码已经在 Github 上开源了 官方地址&#xff1a;GitHub - skylot/jadx: Dex to Java decompiler 发现flag …...

深入解析下oracle date底层存储方式

之前我们介绍了varchar2和char的数据库底层存储格式&#xff0c;今天我们介绍下date类型的数据存储格式&#xff0c;并通过测试程序快速获取一个日期。 一、环境搭建 1.1&#xff0c;创建表 我们还是创建一个测试表t_code&#xff0c;并插入数据&#xff1a; 1.2&#xff0c;…...

Elasticsearch 开放推理 API 增加了对 IBM watsonx.ai Slate 嵌入模型的支持

作者&#xff1a;来自 Elastic Saikat Sarkar 使用 Elasticsearch 向量数据库构建搜索 AI 体验时如何使用 IBM watsonx™ Slate 文本嵌入。 Elastic 很高兴地宣布&#xff0c;通过集成 IBM watsonx™ Slate 嵌入模型&#xff0c;我们的开放推理 API 功能得以扩展&#xff0c;这…...

如何搭建一个小程序:从零开始的详细指南

在当今数字化时代&#xff0c;小程序以其轻便、无需下载安装即可使用的特点&#xff0c;成为了连接用户与服务的重要桥梁。无论是零售、餐饮、教育还是娱乐行业&#xff0c;小程序都展现了巨大的潜力。如果你正考虑搭建一个小程序&#xff0c;本文将为你提供一个从零开始的详细…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...