VectorCAST软件自动化测试方案 博客园
软件测试面临的问题
有一句格言是这样说的,“如果没有事先做好准备,就意味着做好了 失败的准备。”如果把这个隐喻应用在软件测试方面,就可以这样说“没有测试到,就意味着测试失败了”。软件出现故障的代价是巨大的:比如,烤箱或洗碗机不 能正常运作,产品就可能会失去顾客;医疗器械出现故障,病人就可能会失去生命。很多组织想通过严格的开发流程和静态分析来提高软件质量,但还是无法保证软 件应用程序一定不会出现问题。
修复已部署的应用程序中存在的问题所需的费用,远比在代码开发阶段对这些问题进行修复所花的费用高。而众所 周知的是,严格的测试流程对于整个软件开发过程是大有裨益的,因此很多公司都尽力想办法度量测试的完整性。
度量现有测试完整性的一个最简单的方法就是在测试过程中增加代码覆盖率分析。
VectorCAST™ 是主要用于对C/C++/Ada程序进行软件自动化测试,并能够在Windows和Linux等多种开发环境下运行。其主要功能包含自动化的单元测试、集 成测试、覆盖率分析、回归测试、代码静态分析和测试需求跟踪,它最大特点或相比于其它相关同类工具最大的优势,就在于能够实现最大程度的自动化而且更适用 于嵌入式环境。以下分别予以阐述:
单元测试 单元测试就是说针对用户C/C++代码, 每个函数作为一个测试单元,通过编写不同的test case, 来完成代码的单元测试,检测出代码中是否有死循环,多余的变量, 是否有功能和逻辑错误,是否有语法错误等等。
VectorCAST 会根据被测代码和开发环境自动构建测试套件(Test Harness),在此基础上用户可以根据需要自动地生成或添加测试用例,并灵活地配置以实现不同的测试需求。自动生成的架构中包括:被测对象、测试驱动、桩函数和依赖条件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H3viT8OE-1690812113892)(http://bbs.51testing.com/forum.php?mod=image&aid=102124&size=300x300&key=6ed88866f55530e5&nocache=yes&type=fixnone)]
VectorCAST 支持用户以多种形式的输入输出参数自动地生成测试用例,不需要用户编写测试代码,完全通过 鼠标和输入窗口 即可自动完成:
最大值、最小值和中间值
二进制、十进制、八进制和十六进制数值
输入参数范围
多个参数自动排列组合及配对
等价类划分
输入值列表
期望输出值列表
期望值容错误差范围
从外部数据源,如xlc, csv 等,中导入测试用例参数
VectorCAST 可以根据不同的覆盖率的算法分析测程序逻辑,自动地生成满足不同覆盖率要求的测试用例,以尽可能地达到覆盖率要求。对于未能覆盖的部分,用户可以根据工具 提供的逻辑分析报告,如基本路基逻辑和MC/DC 等价类矩阵,用户按照分析结果提示的参数条件额外添加少量测试用例即可达到100%的覆盖率。
基本路径覆盖 — 自动生成用例通常可以自动达到90%~100%的覆盖率
MC/DC覆盖 — 自动生成用例覆盖率视测代码实际情况而定
被测函数所调用的其它函数可以通过形式被执行,被调函数本身或桩函数。从全部调用实际函数到全部由桩函数替代,用户可以根据自己需要灵活设定需要打桩的对象。VectorCAST 可以支持添加不同输入输出的桩函数,简单的如int, float, double, char 等基本类型变量,也可以支持非常复杂的结构或对象变量。重要的是,这些桩函数基本都只需GUI 操作即可完成,一般不需要人工编写测试代码。
VectorCAST 对测试用例的执行方式采用真正的数据驱动方式(Data-Driven),所谓数据驱动,就是指测试用例数据只有在测试用例执行的时候才会被读取,而不会随测试代码、桩函数和测试驱动等等一起构建到最终的执行程序中。这样的好处是:
1)构建和执行速度快;
2)当被测对象未变动的情况下,对测试用例的输入输出参数进行修改,测试用例不需要重新编译构建。
测试用例执行完之后,VectorCAST 除了能对被测单元的实际return 值进行断言以外,包括简单类型和覆盖结构体,还可以对它依赖关系中的变量结果值或其成员变量进行验证,报告True或False 断言结果。
测试用例执行结束之后,VectorCAST 会统计相关程序的 圈复杂度,并自动生成报告。
测试结束后,VectorCAST 自动生成详细的测试报告,包括所有测试用例、测试用例输入输出数据、断言结果、程序度量分析结果、不同覆盖率报告和MC/DC 等价类分析矩阵。
对任意复杂度的C/C++代码自动生成完整的测试驱动和桩函数(无需编写测试代码);
测试驱动支持复杂测试场景,包括同一测试用例中连续调用不同函数自动打桩能够获取输入,控制任何预定义或者用户定义类型的输出;
支持TDD(Test-Driven Development)开发模式,即在未开发.c和.cpp源码文件的情况,能仅根据.h头文件就能自动生成测试用例;
支持测试动态库函数,即针对动态库的头文件所包含的接口生成测试用例,并执行和统计执行结果;
支持根据C/C++程序的.c和.cpp文件自动生成测试环境和测试用例;
树状图形测试用例编辑器使用户方便创建和编辑测试用例;
易于创建测试用例
测试静态,保护和私有函数
构建任意复杂的类的对象
测试多态性和动态分配
抛出和捕获不同类型和值的异常
测试复杂的类的继承
单独的测试模板例程
捕捉和报告不期望的输出和信号
命令行接口允许通过脚本执行所有功能
提供GUI菜单支持用户直接选择根据被测程序的基本路径覆盖率要求自动生成测试用例;
自动分析如何达到被测程序基本路径覆盖的逻辑,并显示分析结果;
提供GUI菜单支持用户直接选择根据被测程序的MC/DC覆盖率要求自动生成测试用例;
自动分析如何达到被测程序的MC/DC覆盖率的逻辑,并显示分析结果;
支持对被测程序的输入参数的多个输入值进行全排列组合和配对来自动生成测试用例;
支持通过GUI窗口添加测试用例输入和期望值时,并自动根据参数的类型和属性检测所填入的值的有效性,如无效,则可提示错误;
自动生成桩函数,提供GUI窗口支持用户设定桩函数返回值,支持复杂类型的返回值;
测试用例构建----不需要编写测试代码,参数和全局数据的值(被测单元和桩)都可以在GUI中定义;
执行----能够在主机平台或者目标模拟器上运行,也可以直接在嵌入式目标机上运行。执行平台可以通过GUI进行控制;
以数据驱动的方式执行测试用例,即仅修改测试用例的输入和期望输入值时,测试环境不需要重新编译;
关于测试用例执行结果的断言,除了对函数的返回值进行期望值验证以外,还可以对被测程序的内部变量或全局变量,通过GUI窗口方便地设定期望值,并在测试执行后自动断言;
测试用例被执行后的结果以不同颜色显示在GUI中;
代码覆盖率----对代码标注颜色进行显示,覆盖级别包括语句、分支和MC/DC级别的覆盖;
支持对系统测试覆盖率的统计;
支持对单元测试和集成测试覆盖率的统计;
支持自由组合或累积多次测试执行结果来查看叠加的覆盖率,而不需要通过将这多次测试的测试用例重新执行来获得;
可以通过自动动画单步回放测试覆盖过程;
提供GUI菜单支持用户直接按DO-178B的A~C等级选择覆盖率统计标准;
支持在多台机器上分布式地执行回归测试用例;
支持统计回归测试用例的执行时间;
支持与常用需求管理系统的集成,导入测试需求,并到处测试用例信息和执行结果到需求管理系统;
通过TUV的认证,并提供认证证书;
支持DO-178B,ISO26262,IEC61508,FDA,IEC62304和CENELEC等不同行业标准的测试需求
提供DO-178B/C认证所需的认证包材料;
提供浮动的使用许可证(License);
VectorCAST/C++:用于C/C++语言程序的单元测试和覆盖率分析
VectorCAST/Cover:统计系统测试的覆盖率,提供图形化界面和命令行两种使用模式
VectorCAST/RSP:支持在嵌入式环境的模拟器和目标机上执行测试并获取测试结果
VectorCAST/MCDC:支持MC/DC覆盖率分析
VectorCAST/Manage:管理自动化的回归测试
VectorCAST/RGW:集成DOORS等需求管理系统,将需求导入到VectorCAST,并将关联的测试用例的执行结果导出达到需求管理系统
VectorCAST/Lint: C/C++程序的静态编码规范检查
VectorCAST Qualification Kits:Do-178B等认证所需的验证材料
传统的开发模式(比如:瀑布式开发、V模式开发、较小程度的敏捷开发)由不同阶段组成。最先进行持续集成工作的应该是编码阶段,在该阶段,开发人员根据分派给自己的任务来编写代码。随着开发的不断进行,开发人员很可能要对存储库进行修改,但是他们只是更新自己开发的代码,不会同时更新其它开发人员的修改。所以,在所有的代码都开发完成之后,会有一个特别的集成阶段,将所有开发人员对代码所作的修改进行整合,以形成一个可测试的软件构建。
我们可以估算出该阶段的开始时间(根据对每项任务的判断进行估算),但是很难预测出截止日期,也就是说很难知道完成集成工作需要花费多长时间。开发人员编写的代码可能会相互冲突:每个开发人员编写的代码在独立运行时,也许能够运行得非常好,但是当该代码和其他开发人员的代码结合在一起时,可能就会发现它不能与其它开发人员对代码所作的修改相兼容。这种冲突必须要解决 —— 通常情况下,这都需要手动解决(也有工具可以帮忙),然后还必须重构代码。但是,代码重构过程中也可能会引进更多的矛盾,这样集成工作就会陷入不断重复的状态。
集成是一项非常重要的工作,在极端情况下,解决冲突所耗的精力要远远超乎一开始编写代码时所花费的精力。
这些可能出现的冲突和由此产生的工作量是开发人员不愿意更新存储库中的局部代码的原因之一:更新可能会破坏现在可以正常运行的代码构建。有些问题可能是其他开发人员的代码引起的,所以开发人员就不想花时间去解决这些由其他人的代码引进的问题。
有好多种解决代码冲突的方法。其中一种方法就是改善开发步骤:每次只允许一个开发人员对存储库进行修改。对存储库中的代码进行更新之后,要立刻解决所有的新问题,这样开发人员要做的就不仅仅只是提交代码那么简单了。这种方法可以避免冲突,但是代价很高。开发人员在提交代码的时候,必须保证只有他一个人可以访问这个存储库:在该开发人员解决好所有冲突之前,不能够有任何其它代码提交进来。(否则,所有的提交工作都要重新开始。)这种排外的访问限制有很大的瓶颈,而且很难实行:有些公司甚至利用实体对象来提示是否允许提交:如果桌子上没有该实物就不可以提交。如果团队比较大,不是只有几个开发人员,那么该方法就很难实施,如果团队分布在不同地区,那么这种方法就更不可行了。
完成集成之后就会生成供测试阶段使用的可执行程序——可能会有很多测试阶段——比如,编码规范合规检查,单元测试,集成测试,系统测试,覆盖率测试,验收测试,部署测试等。在各个测试阶段,如果测试失败,表示代码需要重新编写、重新集成,并且重新测试。这样会有一个潜在的问题,就是开发人员在编写代码时所花费的时间与重写代码时所花费的时间不同:这两者之间的时间差越大,开发人员重新熟悉这些代码的难度越大,因而修改阶段的工作量也会大大增加。
问题的根源在于对开发阶段的划分。软件的编写不是一个单向过程:不是集成工作一开始,编码工作就结束了。虽然将开发任务划分给可以平行地开展工作的开发人员相对而言比较简单,但是集成往往是系列性的,所以要将集成工作同时分配给多个开发人员就比较困难了。
解决方案
正如持续集成所推崇的那样,这个解决方案的部分内容就是用一系列小阶段(如图1)取代传统的编码测试、单元测试、集成测试、系统测试和验收环节。每个小阶段都包含编码测试、单元测试、集成测试、系统测试和验收环节——开发人员每次对存储库进行修改时都要执行这些步骤。每次更改之后都要对代码进行集成、测试和验证,代码每通过一项测试,其质量就越接近于可发布版本.
将集成工作扩展到整个开发阶段意味着繁重的集成工作被分割成了一个个更小、更易操作的任务。当问题能够尽早被发现并独立解决时,就不会得到恶化,因而就不可能形成严重的集成问题——所谓的“集成地狱”。这就是持续集成。可以将这个过程视为从瀑布式向V模型开发模式发展,从敏捷开发向持续集成发展。
这种方法只有在大多数测试过程和任务都可进行实现自动化操作的情况下才可行。因为如果每次存储库中有增量提交时,都要手动完成那些任务,很明显是不可行。
提交之前要进行测试
持续集成模式存在的一个问题就是,它无法防止不能运行的、坏的或不兼容的代码进入存储库中:这种模式只能做到在这类代码提交之后,立刻发现它们。开发人员在处理及修复这类代码造成的问题时,必须严格遵守规则。
在提交之前对代码进行测试的做法是为了保证只有正常的、可运行的、经过测试的代码才被提交到核心存储库中(存储待发布代码的库),因此,这一做法也是实现持续交付的关键所在。这一概念就是说,只有能够发布的代码才能进入存储库中——所以,在提交代码之前,必须对代码进行全面测试。虽然有很多方法可以帮助实现这一目标,但是最常见的做法就是保证未经测试的代码绝不会进入核心存储库。另一个方法就是同时使用临时存储库和核心存储库:在这里只是简单介绍一下这种方法,本文一直会提到这种方法。
当开发人员向存储库中提交代码时,代码不会自动提交到正式存储库,而是会到临时库中。这种做法对于开发人员来说很容易理解:他们提交代码时,和向正式存储库中提交代码一样。持续集成服务器会从临时库中检测到新提交的代码,然后构建并测试这些代码。如果这些代码能够通过所有的测试,那么本次提交就会被转移到正式存储库中,同时也会附上提交者的名字和日志文件。这个做法对于开发人员来说也很好理解:因为就和他们将代码直接提交到了正式存储库中一样(只是时间上有点滞后)。
如果代码未能通过所有的测试,那么本次提交就会保留在临时库中。开发人员会收到通知:他们可以采取必要的补救措施来修复存在的问题,而此时正式存储库中的代码则保持不变。
这一过程要求所有的测试都能够自动执行,而且定义明确。随着项目的成熟,测试也可能会有所改变:比如,贝塔测试可能主要着眼于基本功能,而对于待发布的代码会有更严格的测试标准,在维护阶段则会更加注重测试新功能。
代码覆盖率分析是指在应用程序的测试过程中,将程序的源代码与生成追踪数据的“面包屑”交织起来。这些数据经过后期处理,会显示哪些源代码在某次或某组测试过程中被执行了。代码覆盖率分析可帮助开发人员解答一些关键问题,如:
是否该应用程序的所有部分都已经过测试
是否有不应该存在于该程序中的死码
自动化的代码覆盖率分析非常重要,因为它有以下优势:
可以轻松融入正常的软件开发过程中
不影响正在进行的测试程序和进程
选项可灵活部署,以涵盖测试的所有阶段
软件测试有很多种类,本文将其简要的分为三类:
> 系统/函数测试: 测试集成后的整个应用
> 集成测试::测试集成的子系统
> 单元测试:测试一个或多个文件或类
每个软件项目在系统测试的过程中都会模拟最终用户的操作对源代码做一些系统测试。导致软件发布后仍旧存在缺陷最重要的一个原因通常是程序在运行过程中遇到了非预期的,即没有测试的输入组合。
很多软件项目并不是没有做集成测试或者单元测试。只是在完成集成测试或单元测试后,开发团队可能苦于为隔离程序中的单个或多个文必须所需的大量测试代码量。
对于最严格的单元测试和集成测试来说,最终生成的测试代码量比待测代码量还要庞大是很经常出现的情况。因此,这两种级别的测试普遍适用于关键和高安全领域,例如:航空航天、医疗、交通运输、工业过程控制、高速汽车等。此类软件中包含大量的嵌入式应用软件。
单元测试使用包含驱动和桩的测试代码隔离系统中的特定函数,同时使用测试用例模拟这些函数的执行。这些所谓的“低等级测试需求” 对被测试代码提供了更高的控制,可以提高先前执行的系统测试覆盖率(甚至能达到100%)。因此,在不同的测试之间共享覆盖率数据是非常有必要的。
嵌入式环境中获取覆盖率带来的挑战
常言道“有得必有失”,在嵌入式环境获取代码覆盖率的问题上,要付出的代价是对待测代码额外的插桩工作。
插桩是将额外的代码添加到程序中,从而实现测试过程中的覆盖率收集和分析操作。
由于插桩的相关操作将导致程序