【软件测试】软件测试分类与方法解析:目标到工具
文章目录
- 一、为什么要对软件测试进行分类?
- 二、按照测试目标分类
- 1. 界面测试
- 主要测试项
- 流程图
- 2. 功能测试
- 测试类型与方法
- 流程图
- 主要测试项
- 3. 性能测试
- 关键测试项
- 流程图
- 4. 可靠性测试
- 关键测试项
- 测试流程
- 5. 安全性测试
- 关键测试项
- 测试流程
- 6. 易用性测试
- 关键测试项
- 测试流程
- 三、按照执行方式分类
- 1. 静态测试
- 2. 动态测试
- 3. 对比:静态测试与动态测试
- 四、按照测试方法
- 1. 白盒测试
- 测试方法:覆盖
- 语句覆盖(Statement Coverage)
- 判定覆盖(Decision Coverage)
- 条件覆盖(Condition Coverage)
- 判定条件覆盖(Decision/Condition Coverage)
- 路径覆盖(Path Coverage)
- 总结
- 2. 黑盒测试
- 测试内容
- 常用测试方法
- 3. 灰盒测试
- 测试视角
- 测试方法与技术
- 4. 总结比较差异
- 五、按测试阶段分类
- 单元测试
- 集成测试
- 系统测试
- 六、冒烟测试
- 基本概念
- 执行流程
- 测试用例设计原则
- 冒烟测试 vs 回归测试
- 七、工具
- C++ 测试工具
- 1. Google Test (gtest)
- 2. Catch2
- 3. Boost.Test
- 4. CppUnit
- 5. Doctest
一、为什么要对软件测试进行分类?
软件测试是软件命周期中的⼀个重要环节,对于软件测试,可以从不同的角度进行分类,使开发者在软件开发过程中的不同层次、不同阶段对测试⼯作进行更好的执行和管理测试的分类方法。
二、按照测试目标分类
在软件开发过程中,测试的目标可以依据不同的测试需求和关注点进行分类。不同类型的测试关注不同的质量属性,帮助团队发现潜在问题并改进软件的各个方面。
以下是按照测试目标分类的几种常见测试类型。
1. 界面测试
界面测试(UI 测试)主要关注软件的用户界面部分,确保其符合设计规范并提供良好的用户体验。它检查界面的布局、交互、视觉效果、响应速度等方面,验证软件在不同分辨率、操作系统和设备上的兼容性。
核心目标
- 功能正确性: 界面元素(按钮、链接、输入框、菜单等)是否能按预期工作,响应用户操作。
- 视觉一致性: 界面布局、颜色、字体、图标、间距等视觉元素是否符合设计规范,在不同屏幕和状态下保持一致。
- 易用性与直观性: 界面是否易于学习和使用?导航是否清晰?信息组织是否合理?用户能否轻松找到所需功能和信息?
- 兼容性: 界面在不同浏览器、操作系统、设备(桌面、平板、手机)以及不同屏幕尺寸和分辨率下是否能正确显示和操作。
- 可访问性: 界面是否可以被残障人士(如视觉障碍、听觉障碍、运动障碍)使用?是否符合无障碍标准(如WCAG)。
- 响应性和性能: 界面操作是否流畅?加载时间是否合理?动画效果是否平滑?是否会出现卡顿或无响应?
- 内容准确性: 界面上显示的所有文本、图片、标签、提示信息、错误信息是否准确、无歧义、无拼写错误。
主要测试项
-
布局和元素:
- 控件位置和大小: 按钮、输入框、下拉菜单等是否在正确位置?大小是否合适、一致?
- 对齐和间距: 元素之间是否对齐(左对齐、右对齐、居中对齐)?间距是否均匀、符合设计规范?
- 元素可见性: 该显示的元素是否显示?不该显示的元素(如未激活状态)是否隐藏?
- 滚动条: 内容过长时滚动条是否正常出现和可用?
-
功能和交互:
- 控件功能: 按钮点击、链接跳转、输入框输入、下拉选择、单选/复选框选择、滑块拖动等是否正常工作?
- 表单验证: 输入错误格式时,是否给出清晰、友好的错误提示?提示位置是否合理?
- 导航: 菜单导航、面包屑导航、返回按钮、页面跳转是否顺畅、正确?
- 状态变化: 鼠标悬停(Hover)、点击(Active)、禁用(Disabled)、选中(Selected)等状态的视觉反馈是否正确?
- 键盘操作: 是否支持键盘快捷键?Tab键切换焦点顺序是否合理?Enter/Space键激活控件是否正常?
-
内容和文本:
- 文本准确性: 所有静态文本、动态文本、标签、标题、提示信息、按钮文字、错误信息等是否准确无误?无拼写、语法错误?
- 语言和本地化: 在多语言环境下,翻译是否准确?文本是否因长度变化导致布局错乱?(如德语通常较长)
- 图片和图标: 图片是否清晰、正确显示?图标含义是否清晰?Alt文本是否准确描述图片内容(利于可访问性和SEO)?
-
视觉设计和风格:
- 颜色: 颜色使用是否符合设计规范?前景色和背景色对比度是否足够(尤其考虑可访问性)?颜色是否在不同状态下正确变化?
- 字体: 字体类型、大小、粗细、颜色是否一致且符合规范?文本是否清晰易读?
- 图片/图标风格: 视觉元素风格是否统一?
- 动画和过渡效果: 动画是否流畅?是否必要?是否干扰用户操作?
-
响应式设计:
- 在不同屏幕尺寸(桌面、平板、手机)和分辨率下,界面布局是否能自适应调整?元素是否不会溢出、重叠或变得过小无法操作?
- 横屏/竖屏切换时,界面表现是否正常?
-
可访问性:
- 是否提供足够的颜色对比度?
- 是否支持屏幕阅读器(如正确的语义标签、ARIA属性)?
- 所有功能是否都能通过键盘操作完成?
- 表单字段是否有清晰的标签关联?
- 是否有闪烁或快速移动的内容可能引发光敏性癫痫?
流程图
下面是一个Mermiad图表,对于界面测试的常用方法与目标:
2. 功能测试
功能测试旨在验证软件功能是否符合需求规格说明书(SRS),正确实现了预定的功能。它聚焦于输入、处理逻辑和输出结果的正确性。 是否符合预期结果。
包括:
- 行为正确性:功能逻辑符合业务规则
- 数据完整性:输入/输出数据准确无丢失
- 系统稳定性:异常处理与边界场景可靠
- 兼容性:功能在不同环境中的一致性
测试类型与方法
测试类型 | 说明 | 典型方法 |
---|---|---|
黑盒测试 | 基于需求验证输入输出 | 等价类划分、边界值分析 |
白盒测试 | 验证代码逻辑路径 | 语句覆盖、路径覆盖 |
回归测试 | 确保修改后原有功能正常 | 自动化测试套件 |
集成测试 | 模块间接口交互验证 | API测试、数据流跟踪 |
流程图
- 端到端测试流程
- 缺陷生命周期
主要测试项
3. 性能测试
性能测试用于评估软件在特定负载和压力条件下的响应速度、稳定性和资源使用情况。通过性能测试,团队可以识别潜在的性能瓶颈,确保系统在高并发、高负载等极端条件下依然能够稳定运行。
主要目标:
- 响应时间测试:测量系统在正常或高负载情况下的响应时间,确保软件在用户操作时响应及时。
- 负载测试:评估系统在一定并发用户数下的性能,检查系统是否能处理预期的负载。
- 压力测试:模拟极端负载下的系统表现,测试系统的稳定性和崩溃恢复能力。
- 资源利用测试:检查系统在不同负载下对CPU、内存、磁盘、网络等资源的使用情况,识别潜在的资源瓶颈。
要进行软件产品的性能测试,要对产品的性能需求进行分析,然后基于系统的性能需求和系统架构,完成性能测试的设计和执行,最后要进行持续的性能调优
关键测试项
下面是一些测试类型:
测试类型 | 模拟场景 | 核心指标 |
---|---|---|
负载测试 | 预期用户量 | 响应时间、吞吐量 |
压力测试 | 超出系统极限 | 错误率、资源饱和点 |
并发测试 | 多用户同时操作 | 线程阻塞、死锁发生率 |
稳定性测试 | 7x24小时持续运行 | 内存泄漏、性能衰减率 |
配置测试 | 不同硬件/参数组合 | 最优资源配置方案 |
流程图
4. 可靠性测试
可靠性(Availability) 即可用性,是指系统正常运行的能力或者程度,一般用正常向用户提供软件服务的时间占总时间的百分比表示。
可靠性=正常运行时间/(正常运行时间+非正常运行时间)*100%
主要目标:
- 稳定性验证:测试软件在长时间运行下是否能够保持稳定,检查是否出现崩溃或挂起等问题。
- 容错性验证:验证系统在遇到错误或异常情况时是否能够正确处理并保持运行,不影响其他功能。
- 恢复能力:测试软件在崩溃后是否能够快速恢复,并保证数据的一致性和完整性。
- 错误日志和监控:检查系统是否能准确记录错误信息并提供调试支持,确保团队可以及时定位和修复问题。
关键测试项
测试流程
5. 安全性测试
安全性测试的目标是发现软件中的安全漏洞和风险,确保系统的数据、网络以及用户隐私不被泄露、篡改或破坏。它涵盖了各种攻击手段和威胁模型,以确保系统能够抵御常见的安全威胁。
系统常见的安全漏洞和威胁如下:
- 输入域,如输入恶性或者带有病毒的脚本或长字符串;
- 代码中的安全性问题,如SQL/XML注入
- 不安全的数据存储或者传递
- 数据文件,邮件文件,系统配置文件等里面有危害系统的信息或者数据;
- 有问题的访问控制,权限分配等
- 假冒ID:身份欺骗
- 篡改,对数据的恶意修改,破坏数据的完整性
安全性测试的方法有代码评审,渗透测试,安全运维等,常用的静态安全测试⼯具有:
Coverity,IBM Appscan Source,HPFortify,常用的动态安全测试有OWASP的ZAP,HP WebInspect等。其中静态安全测试是常用的安全性测试的⽅法。
关键测试项
测试流程
flowchart TDA[目标侦察] --> B[漏洞扫描]B --> C{发现漏洞?}C -- 是 --> D[人工验证]C -- 否 --> E[生成报告]D --> F[漏洞利用]F --> G[权限提升]G --> H[数据渗透]H --> I[痕迹清理]I --> J[编写渗透报告]J --> K[修复验证]
6. 易用性测试
易用性测试关注用户与软件交互的友好性和便利性。它评估软件的界面是否符合用户的操作习惯,功能是否直观易用,以及软件在不同用户群体中的适应性。
易用性包含七个要素:符合标准和规范,直观性,一致性,灵活性,舒适性,正确性和实用性。
我们主要讨论以下几个方面:
分类 | 主要内容 | 目标 | 例子 |
---|---|---|---|
标准性和规范性 | 系统是否符合行业标准、规范和最佳实践 | 确保用户可以按照预期方式操作,减少混淆或错误 | 电子商务网站的结账流程符合常见的操作规范(如填写地址、支付方式等) |
直观性 | 系统设计是否简单易懂,用户能轻松理解如何使用 | 让用户无须额外学习即可理解操作,符合常理和预期行为 | 手机应用滑动屏幕切换图片,符合大多数用户的操作习惯 |
灵活性 | 系统能否适应不同用户的需求,支持多种操作方式和定制选项 | 满足不同用户需求,提供多种完成任务的方式,支持个性化设置 | 文件管理软件支持不同视图(图标视图、列表视图),并支持快捷键、拖放操作 |
舒适性 | 用户在使用系统时是否感到愉悦,避免过度繁琐和造成疲劳 | 提供愉快的使用体验,保证长时间使用时的舒适感,避免过度信息加载和繁琐操作 | 健身应用允许调整界面亮度和字体大小,保证舒适的视觉体验 |
关键测试项
测试流程
三、按照执行方式分类
在软件测试中,按照执行方式的不同,测试可以分为静态测试和动态测试。这两种测试方法各有其特点和应用场景,通常在软件开发生命周期中互为补充,共同保障软件质量。
1. 静态测试
静态测试是指在不执行程序的情况下,通过检查软件的文档、代码、设计等静态工件来发现潜在的缺陷。
静态测试侧重于评审、分析和检查,而不涉及代码的运行。它通常在软件开发的早期阶段进行,有助于在代码编写前发现问题,从而减少后期的修复成本。
主要形式:
- 代码审查:开发人员和测试人员共同审查代码,寻找潜在的错误或不符合规范的地方。通过团队成员的互相检查,发现不易察觉的缺陷。
- 静态分析工具:利用自动化工具对源代码进行分析,识别潜在的错误、内存泄漏、代码规范问题等。这些工具能够静态检查代码的质量,提供代码复杂度、可维护性等指标。
- 需求文档评审:检查需求文档的完整性、清晰性和一致性,确保软件需求能够准确传达给开发和测试团队,并能满足用户需求。
- 设计评审:对系统设计文档进行评审,确保设计方案符合需求且架构合理。
主要目标:
- 提早发现缺陷:通过静态方式识别潜在的设计和实现问题,早期发现缺陷有助于减少后期的修复成本。
- 代码质量提升:静态分析可以帮助团队保持代码规范、提高代码的可读性和可维护性。
- 确保一致性:静态测试帮助检查需求、设计与实现之间的一致性,防止需求不明确或设计不合理的情况发生。
- 提高团队合作:通过代码审查和文档评审等形式,团队成员能够共享知识,提升整个开发和测试团队的技能。
2. 动态测试
动态测试是指在程序执行的过程中,通过运行软件,观察其实际行为来发现缺陷。
动态测试关注的是系统在运行时的表现,如功能的正确性、性能的稳定性和响应的及时性等。与静态测试不同,动态测试涉及实际的代码执行,因此能够模拟真实用户的操作,从而验证软件的实际效果。
主要形式:
- 单元测试:对单个模块或组件进行独立测试,验证其是否按照设计和需求正确工作。通常由开发人员编写,自动化执行。
- 集成测试:在多个模块或系统组件集成后,进行的测试,验证这些模块之间的交互是否符合预期。重点是验证模块间的数据传递、接口调用等。
- 系统测试:对整个软件系统进行全面的测试,验证软件系统是否满足所有需求和设计要求。系统测试一般是由独立的测试团队执行。
- 回归测试:在软件修改后进行的测试,确保新增或修改的代码没有破坏系统的其他部分,保证旧功能继续正常工作。
- 验收测试:在软件开发完成后,进行的测试,主要验证软件是否满足用户的业务需求,通常由用户或客户参与。
主要目标:
- 验证软件行为:通过动态测试,验证软件在运行时是否按预期工作,能够满足用户的功能需求。
- 发现执行时缺陷:动态测试能够发现运行时出现的缺陷,如崩溃、性能问题、资源消耗等。
- 性能和稳定性评估:动态测试可以通过模拟不同的负载和压力,评估系统的性能、负载承受能力、稳定性等。
- 确保系统兼容性:通过测试软件在不同操作环境下的表现,动态测试能够检查系统的兼容性问题,如在不同操作系统、设备或浏览器上的运行情况。
3. 对比:静态测试与动态测试
特征 | 静态测试 | 动态测试 |
---|---|---|
测试对象 | 代码、文档、设计等静态工件 | 运行中的软件系统及其功能 |
执行方式 | 不执行代码,依赖人工或工具分析 | 需要执行代码,观察系统的行为 |
目的 | 提前发现缺陷,改进设计和代码质量 | 验证功能实现、发现运行时问题 |
主要形式 | 代码审查、静态分析、文档评审等 | 单元测试、集成测试、系统测试等 |
优势 | 能够提早发现问题,降低修复成本 | 可以模拟实际操作,验证功能的正确性 |
局限性 | 不能发现执行时的错误和性能问题 | 只能发现实际运行中的缺陷,难以提前发现潜在问题 |
四、按照测试方法
1. 白盒测试
白盒测试又称为结构测试或逻辑测试,一般用来分析程序的内部结构,针对程序的逻辑结构来设计测试用例进行测试。
白盒测试的测试目的是:通过检查软件内部的逻辑结构,对软件中的逻辑路径进行覆盖测试;在程序不同地方设立检查点,检查程序的状态,以确定实际运行状态与预期状态是否一致。
白盒测试 主要分为 静态a测试 和 动态测试 两种。静态测试常见于桌面检查、代码审查、代码走查、代码扫描工具。
静态测试常见于桌面检查、代码审查、代码走查、代码扫描工具。
测试方法:覆盖
动态测试方法主要包含六种测试方法:语句覆盖、判定覆盖、条件覆盖、判定条件覆盖、条件组合覆盖、路径覆盖。
根据下面的伪代码:
if A and B:action1()if C and D:action2()
我们将依次分析语句覆盖、判定覆盖、条件覆盖、判定条件覆盖、条件组合覆盖、路径覆盖在这个例子中的应用。
语句覆盖(Statement Coverage)
目标: 确保每条语句至少被执行一次。
在这段代码中,至少要测试以下两个语句:
action1()
语句action2()
语句
测试用例:
-
测试用例 1:
A = True, B = True, C = True, D = True
action1()
会执行,action2()
会执行。
-
测试用例 2:
A = False, B = True, C = True, D = True
action1()
不执行,action2()
会执行。
-
测试用例 3:
A = True, B = True, C = False, D = True
action1()
会执行,action2()
不执行。
-
测试用例 4:
A = False, B = False, C = False, D = False
action1()
和action2()
都不执行。
语句覆盖情况:
- 通过适当的测试用例,可以确保每个
action1()
和action2()
语句至少执行一次。
判定覆盖(Decision Coverage)
目标: 确保每个判定(如 A and B
和 C and D
)的真假值都被测试。
在这段代码中,我们有两个判定:
A and B
C and D
测试用例:
-
测试用例 1:
A = True, B = True, C = True, D = True
A and B
为真,C and D
为真,两个判定都被测试为真。
-
测试用例 2:
A = False, B = True, C = True, D = True
A and B
为假,C and D
为真,A and B
被测试为假,C and D
被测试为真。
-
测试用例 3:
A = True, B = False, C = True, D = True
A and B
为假,C and D
为真,A and B
被测试为假,C and D
被测试为真。
-
测试用例 4:
A = True, B = True, C = False, D = False
A and B
为真,C and D
为假,A and B
被测试为真,C and D
被测试为假。
判定覆盖情况:
- 测试了每个判定的真假值(
A and B
和C and D
)的所有可能情况。
条件覆盖(Condition Coverage)
目标: 确保每个条件(即 A
、B
、C
和 D
)的真假值都被测试。
在这段代码中,有四个条件:
A
B
C
D
测试用例:
-
测试用例 1:
A = True, B = True, C = True, D = True
A
、B
、C
和D
都为真,覆盖了四个条件为真的情况。
-
测试用例 2:
A = False, B = True, C = True, D = True
A
为假,B
为真,C
和D
为真,测试了A
为假,其他条件为真。
-
测试用例 3:
A = True, B = False, C = False, D = True
A
为真,B
为假,C
为假,D
为真,测试了A
、B
和C
的组合。
-
测试用例 4:
A = False, B = False, C = False, D = False
A
、B
、C
和D
都为假,测试了四个条件都为假的情况。
条件覆盖情况:
- 测试了每个条件(
A
、B
、C
、D
)的真假值,确保每个条件都至少为真一次,为假一次。
判定条件覆盖(Decision/Condition Coverage)
目标: 确保每个判定和其内部的每个条件的真假值都被测试。
判定条件覆盖结合了判定覆盖和条件覆盖。需要确保每个判定的真假值被测试,并且每个条件的真假值都被测试。
测试用例:
-
测试用例 1:
A = True, B = True, C = True, D = True
A and B
为真,C and D
为真。- 覆盖了两个判定条件为真。
-
测试用例 2:
A = False, B = True, C = True, D = True
A and B
为假,C and D
为真,A
为假,B
为真,C
为真,D
为真。
-
测试用例 3:
A = True, B = False, C = False, D = True
A and B
为假,C and D
为假,A
为真,B
为假,C
为假,D
为真。
-
测试用例 4:
A = True, B = True, C = False, D = False
A and B
为真,C and D
为假,A
为真,B
为真,C
为假,D
为假。
判定条件覆盖情况:
- 确保了每个判定的真假值被测试,并且每个条件(
A
、B
、C
、D
)的真假值也都被测试。
####. 条件组合覆盖(Condition Combination Coverage)
目标: 确保每个条件的所有可能组合都被测试。条件组合覆盖要求测试所有条件的真假值组合。
在这个例子中,条件组合的所有可能组合有16种(每个条件有真和假两种情况,总共有四个条件)。
测试用例:
- 测试用例 1:
A = True, B = True, C = True, D = True
- 测试用例 2:
A = True, B = True, C = True, D = False
- 测试用例 3:
A = True, B = True, C = False, D = True
- 测试用例 4:
A = True, B = True, C = False, D = False
- 测试用例 5:
A = True, B = False, C = True, D = True
- 测试用例 6:
A = True, B = False, C = True, D = False
- 测试用例 7:
A = True, B = False, C = False, D = True
- 测试用例 8:
A = True, B = False, C = False, D = False
- 测试用例 9:
A = False, B = True, C = True, D = True
- 测试用例 10:
A = False, B = True, C = True, D = False
- 测试用例 11:
A = False, B = True, C = False, D = True
- 测试用例 12:
A = False, B = True, C = False, D = False
- 测试用例 13:
A = False, B = False, C = True, D = True
- 测试用例 14:
A = False, B = False, C = True, D = False
- 测试用例 15:
A = False, B = False, C = False, D = True
- 测试用例 16:
A = False, B = False, C = False, D = False
条件组合覆盖情况:
- 涵盖了所有条件(
A
、B
、C
、D
)的所有可能组合。
路径覆盖(Path Coverage)
目标: 确保所有可能的执行路径(即条件组合和控制流路径)都被测试到。路径覆盖要求测试所有可能的执行路径,而不仅仅是测试条件和判定。
路径覆盖情况:
为了实现路径覆盖,需要测试所有可能的执行路径,这意味着我们需要覆盖如下四条路径:
- 路径 1(
A and B
为真,C and D
为真) - 路径 2(
A and B
为真,C and D
为假) - 路径 3(
A and B
为假,C and D
为真) - 路径 4(
A and B
为假,C and D
为假)
测试用例集合:
- 测试用例 1:
A = True, B = True, C = True, D = True
(覆盖路径 1) - 测试用例 2:
A = True, B = True, C = False, D = False
(覆盖路径 2) - 测试用例 3:
A = False, B = True, C = True, D = True
(覆盖路径 3) - 测试用例 4:
A = False, B = False, C = False, D = False
(覆盖路径 4)
总结:
通过这些测试用例,我们确保了每一条可能的执行路径都被执行了,达到了路径覆盖的要求。
总结
- 语句覆盖: 至少执行一次每条语句。
- 判定覆盖: 每个判定(如
A and B
和C and D
)的真假值都被测试。 - 条件覆盖: 每个条件(如
A
、B
、C
和D
)的真假值都被测试。 - 判定条件覆盖: 每个判定的真假值和每个条件的真假值都被测试。
- 条件组合覆盖: 每个条件的所有可能组合都被测试。
- 路径覆盖: 测试所有可能的执行路径。
对于白盒测试:
- 白盒测试主要应用于单元测试阶段
- 先执行静态设计用例的方法,再执行动态设计测试用例的方法设计用例一般
- 使用路径测试,重点模块追加使用逻辑覆盖方法
2. 黑盒测试
黑盒测试 就是在完全不考虑程序逻辑和内部结构的情况下,检查系统功能是否按照需求规格说明书的规定正常使用、是否能适当的接收输入数据而输出正确的结果,满足规范需求。
黑盒测试又称之为数据驱动测试,只注重软件的功能黑盒测试的优点。
不需要了解程序内部的代码以及实现,不关注软件内部的实现。从用户角度出发设计测试用例,很容易的知道用户会用到哪些功能,会遇到哪些问题,锻炼测试人员的产品思维
测试用例是基于软件需求开发文档,不容易遗漏软件需求文档中需要测试的功能。
黑盒测试的缺点是不可能覆盖所有代码。黑盒测试用到的测试方法有,等价类,边界值,因果图,场景法,错误猜测法等。
测试内容
- 功能测试:验证软件是否按照需求规格说明书的要求正确实现了各项功能。例如,对于一个计算器软件,测试人员会检查加法、减法、乘法、除法等基本运算功能是否准确无误,输入不同类型的数据(整数、小数、负数等)时结果是否正确。
- 性能测试:检测软件在不同负载条件下的性能表现,包括响应时间、吞吐量、资源利用率等指标。比如,对于一个网站,会测试在并发访问量逐渐增加的情况下,页面加载时间是否在可接受范围内,服务器的CPU和内存使用率是否过高。
- 兼容性测试:评估软件在不同的操作系统、浏览器、设备、数据库等环境下能否正常运行。例如,测试一款移动应用是否能在iOS和Android系统的不同版本上稳定运行,界面显示是否正常,功能是否可用。
- 安全性测试:查找软件中可能存在的安全漏洞,如SQL注入、跨站脚本攻击(XSS)、认证和授权漏洞等,确保软件能保护用户数据的安全。比如,尝试在登录界面输入恶意的SQL语句,看系统是否会受到SQL注入攻击。
- 可靠性测试:检验软件在规定条件下和规定时间内完成规定功能的能力,测试软件是否会出现崩溃、死机等异常情况。例如,模拟长时间不间断运行软件,观察其是否稳定。
常用测试方法
- 等价类划分:将输入数据的可能值划分为若干个等价类,从每个等价类中选取一个代表性数据作为测试用例。例如,对于一个接受1到100之间整数输入的功能,可划分为有效等价类(1到100之间的整数)和无效等价类(小于1的整数、大于100的整数、非整数等),然后从每个等价类中选取数据进行测试。
- 边界值分析:关注输入输出数据的边界值,因为软件在边界附近往往容易出现错误。比如,对于上述接受1到100之间整数输入的功能,除了测试1和100这两个边界值,还会测试0、101等临近边界的值。
- 决策表法:适用于具有多个输入条件且这些输入条件之间存在相互组合关系,并且每个组合对应不同的输出结果的情况。通过构造决策表,列出所有可能的输入组合及其对应的输出,然后为每一种组合设计测试用例。例如,在一个根据不同会员等级和购买金额计算折扣的功能中,会员等级有普通会员、高级会员、VIP会员,购买金额有不同区间,可使用决策表来覆盖所有可能的情况。
- 因果图法:考虑输入条件之间的因果关系以及输入与输出之间的因果关系,通过绘制因果图来确定测试用例。当软件的功能需求中包含多种条件的组合以及相应的动作时,因果图法可以帮助测试人员更全面地设计测试用例。
3. 灰盒测试
灰盒测试,是介于白盒测试与黑盒测试之间的一种测试,灰盒测试多用于集成测试阶段,不仅关注输出、输入的正确性,同时也关注程序内部的情况。
测试视角
- 部分内部结构可见:
- 测试人员知晓软件内部的一些架构信息,如模块划分、接口情况以及大致的数据流向等。但不像白盒测试那样清楚具体的代码逻辑、算法实现以及变量的使用方式。
- 例如,对于一个Web应用程序,测试人员知道它由用户认证模块、数据处理模块和页面展示模块组成,也了解模块间的接口,但并不清楚每个模块内部的代码细节。
- 功能与结构结合:
- 在测试时,不仅依据软件的功能规格说明书来验证功能是否正确实现,还会利用对内部结构的了解,更有针对性地设计测试用例。
- 例如,知道两个模块之间通过特定接口进行数据交互,就可以重点测试该接口在不同数据输入情况下的稳定性和正确性,同时结合功能需求,检查经过模块处理后输出结果是否符合预期。
测试方法与技术
- 基于架构的测试:
- 根据软件的架构设计文档,分析模块之间的依赖关系、调用层次等,针对这些关系设计测试用例。
- 比如,通过了解软件的分层架构,对不同层次之间的接口进行重点测试,确保数据在各层之间传递的准确性和完整性。
- 接口测试:
- 着重对软件内部模块之间以及软件与外部系统交互的接口进行测试。测试人员需要了解接口的输入输出参数、数据格式、调用规则等。
- 例如,在一个与第三方支付平台对接的电商系统中,对支付接口进行灰盒测试,验证传递的订单金额、商品信息等参数是否正确,接口返回的支付结果是否符合预期。
- 结合功能与内部逻辑:
- 利用对部分内部逻辑的了解来补充功能测试。
- 例如,知道某个功能的实现依赖于特定的数据库查询操作,在功能测试时,可以针对不同的数据库数据状态,设计相应的功能测试用例,检查功能在各种情况下的表现。
4. 总结比较差异
以测试视角以及技术需求的角度总结如下:
五、按测试阶段分类
单元测试
与编码同步进行,针对软件最小组成单元进行测试,主要采用白盒测试方法,从被测对象的内部结构出发设计测试用例。
对于”最小单元“,一个方法,一个类都可以理解为“最小单元”。
- 测试阶段:编码后或者编码前(TDD)
- 测试对象:最小模块
- 测试人员:白盒测试工程师或开发工程师
- 测试依据:代码和注释+详细设计文档
- 测试方法:白盒测试
- 测试内容:模块接口测试、局部数据结构测试、路径测试、错误处理测试、边界测试
比如给下面的冒泡排序实现一个单元测试:
#include <iostream>
#include <vector>
#include <algorithm> // 用于 std::equalusing namespace std;// 冒泡排序函数
void bubbleSort(vector<int>& arr) {int n = arr.size();for (int i = 0; i < n; i++) {// 每一轮遍历将最大的数移到末尾for (int j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {// 交换元素swap(arr[j], arr[j + 1]);}}}
}// 用于比较两个数组是否相等的函数
bool areArraysEqual(const vector<int>& arr1, const vector<int>& arr2) {return std::equal(arr1.begin(), arr1.end(), arr2.begin());
}// 测试 1:排序一个无序的数组
void Test01() {vector<int> act_array1 = {64, 34, 25, 12, 22, 11, 90};vector<int> expect_array1 = {11, 12, 22, 25, 34, 64, 90};// 排序数组bubbleSort(act_array1);// 判断排序后的数组是否与期望结果相同if (!areArraysEqual(act_array1, expect_array1)) {cout << "测试不通过" << endl;} else {cout << "测试通过" << endl;}
}// 测试 2:排序一个已经排序的数组
void Test02() {vector<int> act_array1 = {1, 2, 3, 4, 5};vector<int> expect_array1 = {1, 2, 3, 4, 5};// 排序数组bubbleSort(act_array1);// 判断排序后的数组是否与期望结果相同if (!areArraysEqual(act_array1, expect_array1)) {cout << "测试不通过" << endl;} else {cout << "测试通过" << endl;}
}// 测试 3:排序一个空数组
void Test03() {vector<int> act_array1 = {};vector<int> expect_array1 = {};// 排序数组bubbleSort(act_array1);// 判断排序后的数组是否与期望结果相同if (!areArraysEqual(act_array1, expect_array1)) {cout << "测试不通过" << endl;} else {cout << "测试通过" << endl;}
}// 测试 4:排序一个包含重复元素的数组
void Test04() {vector<int> act_array1 = {1, 1, 29, 12, 12, 9, 9};vector<int> expect_array1 = {1, 1, 9, 9, 12, 12, 29};// 排序数组bubbleSort(act_array1);// 判断排序后的数组是否与期望结果相同if (!areArraysEqual(act_array1, expect_array1)) {cout << "测试不通过" << endl;} else {cout << "测试通过" << endl;}
}int main() {// 运行所有测试Test01(); // 测试无序数组Test02(); // 测试已经排序的数组Test03(); // 测试空数组Test04(); // 测试包含重复元素的数组return 0;
}
集成测试
集成测试也称 联合测试(联调)、组装测试,将程序模块采用适当的集成策略组装起来,对系统的接口 及集成后的功能进行正确性检测的测试工作。集成主要目的是检查软件单位之间的接口是否正确。
- 测试阶段:一般单元测试之后进行
- 测试对象:模块间的接口
- 测试人员:白盒测试工程师或开发工程师
- 测试依据:单元测试的模块+概要设计文档
- 测试方法:黑盒测试与白盒测试相结合
- 测试内容:模块之间数据传输、模块之间功能冲突、模块组装功能正确性、全局数据结构、单模块缺陷对系统的影响
系统测试
对通过集成测试的系统进行整体测试,验证系统功能性和非功能性需求的实
现。
- 测试阶段:集成测试通过之后
- 测试对象:整个系统(软、硬件)
- 测试人员:”黑盒测试工程师
- 测试依据:9需求规格说明文档
- 测试方法:黑盒测试
- 测试内容:功能、界面、可靠性、易用性、性能、兼容性、安全性等
六、冒烟测试
基本概念
来源:
术语源自硬件行业。
对一个硬件或硬件组件进行更改或修复后,直接给设备加电。如果没有冒烟,则该组件就通过了测试。
在软件中,“冒烟测试”这一术语描述的是在将代码更改嵌入到产品的源树中之前对这些更改进行验证的过程。在检查了代码后,冒烟测试是确定和修复软件缺陷的最济有效的方法。 冒烟测试设计用于确认代码中的更改会按预期运行,且不会破坏整个版本的稳定性。
冒烟测试的对象是每一个新编译的需要正式测试的软件版本,目的是确认软件主要功能和核心流程正常,在正式进行系统测试之前执行。
定义:
在软件构建(Build)完成后,快速验证核心功能是否可运行的基础测试,确认版本是否具备进一步测试的条件。
别名:
- 构建验证测试(Build Verification Test, BVT)
- 版本健康检查(Build Health Check)
执行流程
测试用例设计原则
冒烟测试 vs 回归测试
对比项 | 冒烟测试 | 回归测试 |
---|---|---|
范围 | 主干核心功能(5-10%) | 全量功能(70-100%) |
耗时 | 分钟级~小时级 | 小时级~天级 |
目的 | 快速验证版本可用性 | 确保修改不破坏既有功能 |
执行阶段 | 构建后立即执行 | 冒烟测试通过后执行 |
七、工具
C++ 测试工具
C++有许多流行的单元测试框架,可以帮助你编写和运行单元测试。以下是一些常用的C++单元测试框架:
1. Google Test (gtest)
Google Test 是 Google 开发的一个流行的 C++ 单元测试框架,支持多种断言、测试夹具(test fixture)、测试参数化等功能。它适用于大多数 C++ 项目,文档丰富,使用广泛。
特点:
- 支持断言(assertions),如
EXPECT_EQ
、ASSERT_EQ
、EXPECT_TRUE
等。 - 测试夹具支持(在测试前后进行初始化和清理)。
- 支持测试参数化,方便对不同的输入进行测试。
- 可以与 Google Mock 一起使用,进行模拟对象的测试。
安装:
你可以通过源码或包管理工具(如 vcpkg
或 brew
)来安装 Google Test。
示例:
#include <gtest/gtest.h>int Add(int a, int b) {return a + b;
}// 测试函数
TEST(AddTest, PositiveNumbers) {EXPECT_EQ(Add(1, 2), 3); // 断言 1 + 2 == 3
}TEST(AddTest, NegativeNumbers) {EXPECT_EQ(Add(-1, -2), -3); // 断言 -1 + -2 == -3
}// main 函数运行所有测试
int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv); // 初始化 Google Testreturn RUN_ALL_TESTS(); // 运行所有测试并返回测试结果
}
2. Catch2
Catch2 是另一个现代的 C++ 单元测试框架,旨在提供一个简洁且易于使用的 API。它没有依赖任何外部库,使用简单,适合快速编写测试。
特点:
- 支持断言(assertions)和测试标记(tags)功能。
- 可以直接在头文件中使用,无需链接。
- 提供了很多内置的断言宏,可以让你在测试中方便地进行检查。
- 支持 BDD(行为驱动开发)风格的语法。
安装
Catch2 可以通过源代码或者包管理工具安装,也可以将头文件直接集成到项目中。
特点:
#define CATCH_CONFIG_MAIN // 需要定义这个宏以生成 main 函数
#include <catch2/catch.hpp>int Add(int a, int b) {return a + b;
}TEST_CASE("Addition of two numbers", "[add]") {REQUIRE(Add(1, 2) == 3); // 断言 1 + 2 == 3REQUIRE(Add(-1, -2) == -3); // 断言 -1 + -2 == -3
}
3. Boost.Test
Boost 是 C++ 中非常著名的一个库,Boost.Test
是其中的单元测试框架。它提供了非常丰富的功能,包括单元测试、性能测试、单元测试的组织结构等。
特点:
- 提供了多种断言和测试类型。
- 具有强大的参数化测试功能。
- 可以与其他 Boost 库一起使用,适合复杂的 C++ 项目。
安装:
你需要先安装 Boost 库,可以通过源码编译或者使用包管理工具(如 vcpkg
或 brew
)安装 Boost。
示例:
#define BOOST_TEST_MODULE TestAdd
#include <boost/test/included/unit_test.hpp>int Add(int a, int b) {return a + b;
}BOOST_AUTO_TEST_CASE(test_add) {BOOST_CHECK(Add(1, 2) == 3); // 断言 1 + 2 == 3BOOST_CHECK(Add(-1, -2) == -3); // 断言 -1 + -2 == -3
}
4. CppUnit
CppUnit 是 C++ 的一个老牌单元测试框架,灵感来源于 JUnit。它支持测试用例、测试套件、测试夹具等,适合需要兼容传统 C++ 工程的项目。
特点:
- 类似于 JUnit 的架构,易于理解。
- 支持测试夹具和断言。
- 支持参数化测试。
安装:
你可以从 CppUnit 的官方网站或 GitHub 获取源码并编译,或者使用包管理工具来安装它。
示例:
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/ui/text/TextTestRunner.h>class TestAdd : public CppUnit::TestFixture {CPPUNIT_TEST_SUITE(TestAdd);CPPUNIT_TEST(testAddition);CPPUNIT_TEST_SUITE_END();public:void testAddition() {CPPUNIT_ASSERT(Add(1, 2) == 3); // 断言 1 + 2 == 3}
};CPPUNIT_TEST_SUITE_REGISTRATION(TestAdd);int main() {CppUnit::TextUi::TestRunner runner;runner.addTest(TestAdd::suite());runner.run();
}
5. Doctest
Doctest 是一个非常轻量级的 C++ 单元测试框架,它的设计目标是非常快速且易于集成。其最大特点是它支持将测试直接嵌入到文档中。你可以将测试代码和文档结合起来,运行文档中的代码片段进行测试。
特点:
- 很小的开销,快速集成。
- 测试可以嵌入到文档注释中。
- 支持断言和检查输出。
特点:
Doctest 只有一个头文件,因此非常容易集成到项目中。
示例:
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>int Add(int a, int b) {return a + b;
}TEST_CASE("Testing Add function") {CHECK(Add(1, 2) == 3); // 断言 1 + 2 == 3CHECK(Add(-1, -2) == -3); // 断言 -1 + -2 == -3
}
框架 | 特点 | 适用场景 |
---|---|---|
Google Test | 功能全面,支持断言、测试夹具、参数化测试等 | 适合大规模项目,使用广泛 |
Catch2 | 易于使用,支持BDD风格,集成简单 | 适合小型或快速开发的项目 |
Boost.Test | 功能强大,支持多种断言,依赖Boost库 | 适合依赖Boost的大型项目 |
CppUnit | 经典框架,类似JUnit,功能齐全 | 适合老旧项目,兼容性好 |
Doctest | 轻量,支持嵌入式测试,快速集成 | 适合嵌入式测试和快速开发 |