【单元测试】--单元测试最佳实践
一、单元测试代码风格
编写单元测试代码时,遵循一致的风格和最佳实践是非常重要的,因为它有助于提高代码的可读性、可维护性和可靠性。以下是一些常见的单元测试代码风格和最佳实践:
- 命名约定:
- 测试方法的名称应当清晰、描述性,反映被测试方法的功能和行为。通常使用"Test"或"Should"前缀。
- 使用下划线或驼峰命名法来分隔单词,例如
Test_CalculateTotalCost
或Should_ReturnValidResult
. - 使用有意义的变量和方法名,以提高代码可读性。
- 测试组织:
- 使用测试类(Test Fixture)来组织相关测试方法,通常一个测试类对应一个被测类。
- 使用测试套件(Test Suite)来组织多个测试类,以便一次运行多个相关测试。
- 断言风格:
- 使用清晰的断言函数来验证测试的期望结果。在NUnit中,这可以是
Assert.AreEqual
、Assert.IsTrue
等。 - 避免多个断言在一个测试方法中,一个测试方法应该验证一个方面的行为。
- 使用自定义的消息参数来描述断言失败时的情境,帮助更好地理解问题。
- 使用清晰的断言函数来验证测试的期望结果。在NUnit中,这可以是
- 准备数据:
- 在
Arrange
(准备)部分,准备测试所需的数据、对象和环境。 - 使用SetUp方法来初始化测试上下文,避免重复的设置。
- 在
- 清理资源:
- 使用TearDown方法来释放测试所需的资源,如关闭文件、数据库连接等。
- 如果使用了外部资源(文件、数据库等),确保测试后资源不会被破坏。
- 注释和文档:
- 提供清晰和简洁的注释,解释测试的目的、涉及的场景和特殊情况。
- 使用XML文档注释(对于支持它的语言,如C#)来生成文档。
- 避免硬编码:
- 避免在测试代码中硬编码常数和魔法值,使用常量或参数化测试来提高可维护性。
- 可读性和一致性:
- 保持一致的缩进、空格和命名约定。
- 使用代码格式化工具来确保一致性。
- 单一职责原则:
- 一个测试方法应该验证一个特定方面的行为,遵循单一职责原则。
- 速度和独立性:
- 测试应该快速执行,以便在持续集成中进行频繁运行。
- 测试之间应该相互独立,不依赖于其他测试的状态。
这些风格和最佳实践有助于确保单元测试代码的高质量和可维护性。保持一致性和编写自解释的测试代码可以帮助整个团队更容易理解和维护测试套件。
二、针对边界条件的测试
在单元测试中,针对边界条件的测试非常重要,因为边界条件通常是软件中出现问题的关键点。使用单元测试框架,你可以编写特定于边界条件的测试用例,以确保代码在这些情况下的行为是正确的。以下是一些针对边界条件的测试的示例(以NUnit为例):
假设你有一个名为MathUtils
的类,其中包含一个方法IsPrime(int number)
,该方法用于检查一个整数是否是质数。
public class MathUtils
{public bool IsPrime(int number){if (number <= 1)return false;for (int i = 2; i <= Math.Sqrt(number); i++){if (number % i == 0)return false;}return true;}
}
现在,让我们编写针对边界条件的测试用例:
using NUnit.Framework;[TestFixture]
public class MathUtilsTests
{[Test]public void IsPrime_WithNegativeNumber_ReturnsFalse(){MathUtils mathUtils = new MathUtils();bool result = mathUtils.IsPrime(-5);Assert.IsFalse(result);}[Test]public void IsPrime_WithZero_ReturnsFalse(){MathUtils mathUtils = new MathUtils();bool result = mathUtils.IsPrime(0);Assert.IsFalse(result);}[Test]public void IsPrime_WithOne_ReturnsFalse(){MathUtils mathUtils = new MathUtils();bool result = mathUtils.IsPrime(1);Assert.IsFalse(result);}[Test]public void IsPrime_WithSmallPrimeNumber_ReturnsTrue(){MathUtils mathUtils = new MathUtils();bool result = mathUtils.IsPrime(2);Assert.IsTrue(result);}
}
这些测试用例覆盖了边界条件:
IsPrime_WithNegativeNumber_ReturnsFalse
测试了负数。IsPrime_WithZero_ReturnsFalse
测试了0。IsPrime_WithOne_ReturnsFalse
测试了1。IsPrime_WithSmallPrimeNumber_ReturnsTrue
测试了最小的质数2。
这些测试有助于确保IsPrime
方法在边界条件下返回了预期的结果。通过编写这些测试,你可以更好地理解代码的行为,同时也确保它正确处理了边界情况。
在编写针对边界条件的测试时,确保考虑到所有可能的情况,包括输入最小值、最大值、边界值以及非法输入。这有助于提高代码的鲁棒性和质量。
三、数据驱动测试
数据驱动测试是一种测试方法,它允许你执行相同的测试代码,但使用不同的输入数据集进行多次测试。这是在NUnit中的一个常见测试模式。以下是如何在NUnit中执行数据驱动测试的示例:
假设你有一个名为MathUtils
的类,其中包含一个方法Add(int a, int b)
,该方法用于将两个整数相加。
首先,你需要为数据驱动测试准备数据。你可以使用不同的输入参数和预期输出创建一个数据源。在C#中,你可以使用TestCaseSource
特性来指定数据源。在这个示例中,我们将创建一个数据源的类AddTestCases
,它包含多个测试用例。
using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;public class AddTestCases
{public static IEnumerable TestCases{get{yield return new TestCaseData(2, 3, 5); // 输入 2 和 3,期望输出 5yield return new TestCaseData(-1, 1, 0); // 输入 -1 和 1,期望输出 0yield return new TestCaseData(0, 0, 0); // 输入 0 和 0,期望输出 0yield return new TestCaseData(10, -5, 5); // 输入 10 和 -5,期望输出 5}}
}
然后,在你的测试类中,你可以使用TestCaseSource
特性指定数据源,并在测试方法中使用参数接收测试数据。
[TestFixture]
public class MathUtilsTests
{[Test, TestCaseSource(typeof(AddTestCases), "TestCases")]public void Add_AddsNumbers(int a, int b, int expected){MathUtils mathUtils = new MathUtils();int result = mathUtils.Add(a, b);Assert.AreEqual(expected, result);}
}
在上述示例中,Add_AddsNumbers
测试方法使用了TestCaseSource
特性,它指定了数据源为AddTestCases
类中的TestCases
属性。这意味着测试方法将使用数据源中的每个测试用例来执行测试。
当你运行这个测试类时,NUnit将自动执行多次测试,每次使用一个不同的测试用例,确保Add
方法在不同输入情况下都返回了正确的结果。
数据驱动测试非常适用于需要测试多组输入参数的情况,同时保持测试代码的简洁性。这有助于确保代码在各种情况下都能正确工作。
四、单元测试的性能考虑
保证单元测试的性能是非常重要的,因为测试过于耗时可能会影响开发流程和持续集成的效率。以下是一些方法,可以帮助你确保单元测试具有良好的性能:
- 编写快速测试:
- 编写快速执行的单元测试,这些测试应该迅速完成,通常在毫秒级别。
- 避免在单元测试中执行大量的复杂计算或访问外部资源,如数据库或网络服务。
- Mock外部依赖:
- 使用模拟(Mock)对象或桩(Stub)来替代外部依赖,如数据库或网络调用。
- 这可以使你的单元测试更快速,因为它们不需要与外部系统通信。
- 并行执行测试:
- 确保你的单元测试能够并行执行,以充分利用多核处理器和提高测试速度。
- 使用支持并行测试执行的测试框架,如NUnit或JUnit。
- 减少I/O操作:
- 尽量减少在单元测试中执行文件读写、数据库访问等I/O操作。
- 使用内存数据库或者模拟文件系统来减少I/O操作的开销。
- 拆分大型测试用例:
- 避免编写过于庞大的测试用例,这样的测试可能会变得缓慢。
- 将大型测试用例拆分成多个小的测试用例,每个测试一个特定的功能或场景。
- 使用性能分析工具:
- 使用性能分析工具,如性能剖析器,来识别测试用例中的性能瓶颈。
- 根据性能分析结果优化测试代码。
- 监控资源使用:
- 监控测试用例的资源使用情况,如内存、CPU等。
- 确保测试用例不会耗尽系统资源。
- 定期重构测试代码:
- 定期重构测试代码以提高其性能。
- 优化测试代码的结构,以减少不必要的重复和计算。
- 关注测试数据:
- 使用合适的测试数据,确保测试覆盖不同情况。
- 使用边界条件和代表性数据进行测试。
- 在持续集成中运行:
- 将单元测试包括在持续集成(CI)流程中,以确保测试在每次代码更改后都得到运行。
- 在CI服务器上并行执行测试,以快速检测潜在问题。
- 设置性能基准:
- 确定性能基准,以监测测试性能是否在可接受范围内。
- 使用性能测试工具来进行基准测试。
- 处理测试用例的遗留问题:
- 针对已存在的测试用例,检查是否有性能问题,并尝试修复。
- 不断更新和优化测试用例,以反映代码和需求的变化。
确保单元测试的性能需要在测试编写阶段考虑性能问题,使用适当的工具和技术来优化测试,以确保测试是高效且可维护的。性能问题的早期识别和解决有助于提高开发效率,减少后期问题的修复成本。
五、总结
单元测试代码风格应当遵循一致的命名约定、测试组织和断言风格。准备测试数据,清理资源,避免硬编码,关注可读性和性能。针对边界条件的测试是关键,确保代码在关键点上正确。数据驱动测试允许使用不同的输入数据多次运行相同的测试代码。保证单元测试的性能需要编写快速测试、模拟外部依赖、使用并行执行、减少I/O操作、监控资源使用等方法。这些实践有助于提高代码质量和可维护性,确保测试在不同情况下都有效。
相关文章:
【单元测试】--单元测试最佳实践
一、单元测试代码风格 编写单元测试代码时,遵循一致的风格和最佳实践是非常重要的,因为它有助于提高代码的可读性、可维护性和可靠性。以下是一些常见的单元测试代码风格和最佳实践: 命名约定: 测试方法的名称应当清晰、描述性&…...

llava1.5-部署
llava1.5 ——demo部署 下载代码和权重 新建weights文件夹,并下载到LLaVA/weights/中。->需要修改文件名为llava-版本,例如llava-v1.5-7b. 运行 启动控制台 python -m llava.serve.controller --host 0.0.0.0 --port 4006启动gradio python -m…...

倒计时 1 天|KCD 2023 杭州站
距离「KCD 2023 杭州站」开始只有 1 天啦 大家快点预约到现场哦~ KCD 2023 活动介绍 HANGZHOU 关于 KCD Kubernetes Community Days(KCD)由云原生计算基金会(CNCF)发起,由全球各国当地的 CNCF 大使、CNCF 员…...

什么是模拟芯片,模拟芯片都有哪些测试指标?
模拟芯片又称处理模拟信号的集成电路 模拟集成电路主要是指由电容、电阻、晶体管等组成的模拟电路集成在一起用来处理模拟信号的集成电路。有许多的模拟集成电路,如运算放大器、模拟乘法器、锁相环、电源管理芯片等。 模拟集成电路的主要构成电路有:放…...

C++-json(2)-unsigned char-unsigned char*-memcpy-strcpy-sizeof-strlen
1.类型转换: //1.赋值一个不知道长度的字符串unsigned char s[] "kobe8llJfFwFSPiy"; //1.用一个字符串初始化变量 unsigned int s_length strlen((char*)s); //2.获取字符串长度//2.字符串里有双引号"" 需要…...

python安装第三方包
1 命令行下载 pip install 包名称 进入命令行输入该命令 由于pip是连接的国外的网站进行包的下载,所以有的时候会速度很慢。 我们可以通过如下命令,让其连接国内的网站进行包的安装: pip install -i https://pypi.tuna.tsinghua.edu.cn/s…...

《数据结构、算法与应用C++语言描述》-队列的应用-电路布线问题
《数据结构、算法与应用C语言描述》-队列的应用-电路布线问题 问题描述 在 迷宫老鼠问题中,可以寻找从迷宫入口到迷宫出口的一条最短路径。这种在网格中寻找最短路径的算法有许多应用。例如,在电路布线问题的求解中,一个常用的方法就是在布…...

GC overhead limit exceeded问题
1.问题现象 程序包运行时候发生了java.lang.OutOfMemoryError: GC overhead limit exceeded异常, 详细信息如下 org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.jboss.util.NestedSQLException: Error; - nested t…...
What‘s new in Arana v0.2.0
Arana 定位于云原生数据库代理,它可以以 sidecar 模式部署为数据库服务网格,项目地址是 https://github.com/arana-db/arana 。Arana 提供透明的数据访问能力,当用户在使用时,可以不用关心数据库的 “分片” 细节,像使…...
STM32 串口接收中断被莫名关闭
使用cubeidestm32f4进行调试,发现UART4串口会被莫名的关掉,导致不能接收数据,经过排查如下: HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) {uint8_t *pd…...

接口测试vs功能测试
接口测试和功能测试的区别: 本文主要分为两个部分: 第一部分:主要从问题出发,引入接口测试的相关内容并与前端测试进行简单对比,总结两者之前的区别与联系。但该部分只交代了怎么做和如何做?并没有解释为什…...
前端面试题整理(1.0)
1.nextTick原理 Vue是异步执行Dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环(event loop)当中观察到数据变化的Watcher推送到这个队列。如果这个Watcher被触发多次,智慧被推送…...

使用Spire.PDF for Python插件从PDF文件提取文字和图片信息
目录 一、Spire.PDF插件的安装 二、从PDF文件提取文字信息 三、从PDF文件提取图片信息 四、提取图片和文字信息的进阶应用 总结 在Python中,提取PDF文件的文字和图片信息是一种常见的需求。为了满足这个需求,许多开发者会选择使用Spire.PDF插件&…...

springBoot整合讯飞星火认知大模型
1.概述 讯飞星火大模型是科大讯飞最近开放的拥有跨领域的知识和语言理解能力的大模型,能够完成问答对话和文学创作等。由于讯飞星火大模型最近可以免费试用,开发者都可以免费申请一个QPS不超过2的账号,用来实现对平台能力的验证。本文将利用…...
JMM对数据竞争的定义
JMM对数据竞争的定义 Java内存模型规范对数据竞争的定义如下在一个线程中写一个变量,在另一个线程读同一个变量,而且写和读没有通过同步来排序。如果一个多线程程序能正确同步,这个程序将是一个没有数据竞争的程序。当程序未正确同步时&…...
民安智库(湖北知名满意度测评公司)食品安全满意度调查如何开展
食品安全问题一直以来都是社会各界广泛关注的焦点之一。近年来,食品安全事件频发,引起了公众的高度关注和担忧。因此,开展食品安全满意度调查,了解公众对食品安全状况的认知和满意程度,对于促进食品安全共建共治共享具…...
Rust 语法笔记
变量绑定(声明变量) let 变量名: 类型 变量值; let 变量名 变量值[类型]; // 整型 默认 i32;浮点 默认 f64所有的 let 绑定都必须尾接;,代码块也不例外。 mut 可以通过重新声明的方式来改变变量类型 可以下划线改善数字的可读…...

AI智慧安防智能监控平台如何做到健身房智能视频监控?
随着大家对健身的重视,健身房也开始遍地开花,健身房的兴起是必然的,但是健身房的管理不容疏忽,通过EasyCVR智能视频监控系统,则可以解决监管不足的问题。 1、安全摄像头布局 根据健身房的大小和布局,合理规…...

ps插件Coolorus for Mac中文激活版
Coolorus是一款非常实用的Photoshop插件,它为Photoshop增加了色环配色面板,让设计师可以更直观地选择颜色。同时,Coolorus还提供了多种专业配色方案,如鲜艳色、复古色、日常色等,设计师可以直接套用这些方案࿰…...

MySQL的索引——索引的介绍及其数据结构B+树 索引的类型 索引的使用及其失效场景 相关名词解释
前言 索引是存储引擎用于快速查找数据纪录的一种数据结构,索引是数据库中经常提及的一个词,究竟什么是索引,索引的数据结构是什么,索引有什么类型? 本篇博客尝试阐述数据库索引的相关内容,涉及什么是索引…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...