• [技术交流] 单元测试/集成测试自动化工具
    单元测试/集成测试自动化工具--WinAMSCoverageMaster winAMS : 适用于嵌入式目标机代码的单元测试/集成测试工具全面支持嵌入式微机!验证嵌入式C/C++软件 实施以模块为单位的自动化单元测试工具不需要HookCode 直接使用目标机代码进行单元测试联合静态解析工具[CasePlayer2],提供C0(语句),C1(判定),MC/DC覆盖率报告,优化测试用例制作已取得第三方认证机构TUVSUD对适用于汽车机能安全ISO26262软件工具的认证产品概要[Coverage master winAMS]是以嵌入式软件的函数为单位,实施模块单元测试以及C0/C1/MCDC覆盖率测试(coverage test)的嵌入式软件自动化单元测试工具。目标机源代码通过交叉编译器生成目标机执行代码,通过跟实际处理器同样的模拟处理器环境进行单元测试,不需要对执行代码做任何变动,使高信赖性的模块测试成为可能。在汽车控制软件这样的对安全性要求极高的领域,单元测试已经成为不可缺少的一部分。使用目标机代码进行单元测试也是为了符合汽车行业中ISO26262功能安全认证标准。产品特长全面支持嵌入式微机!验证嵌入式C/C++软件 实施以模块为单位的自动化单元测试工具作为能够检验出仅凭系统测试以及整体测试无法发现的[潜在错误]的检测方法,[单元测试]在嵌入式开发领域受到广泛重视。同时,单元测试也是汽车用软件功能安全(ISO26262)领域中要求实施的认证项目之一。[Coverage master winAMS]直接使用通过交叉编译生成的目标机代码,在模拟处理器环境下进行单元测试。既能实现C语言程序的逻辑上的单元验证,又能够对嵌入式微机组装为产品后可能发生的问题等进行具有高信赖度的白盒(white box)测试。不需要HookCode 使直接使用目标机代码进行单元测试成为可能的业界唯一的工具有些公司的单元测试工具往往采用在被测试对象的源代码中追加测试用代码或者测试用驱动器的方法,导致测试时所用的代码与组装为产品后的目标机用代码不同。虽然[理论上运行功能应该是相同的],但是从嵌入式开发的角度考虑,这样就如同对交叉编译所生成的经过优化处理的代码进行了加工,无法确保最终产品的质量。Coverage master winAMS是业界唯一的,具有[不需要对被测试对象做任何加工]实施单元测试功能的工具,特别是在安全性要求高的领域中得到很高的评价。不需建立单元测试专用的环境,可以在开发用交叉编译环境进行单元测试Coverage master winAMS不需要追加任何测试用驱动器或测试用代码,可以直接使用将组装成产品的目标代码进行单元测试。单元测试能够与软件开发使用共同的交叉编译环境,不再需要对测试资源进行专门管理,也不再需要建立其他专用环境。因此,既方便程序资源管理,又能够缩短准备测试环境所需的时间。符合汽车功能安全标准(ISO26262)[不做加工直接使用目标机代码实施单元测试]这一要求的最佳工具ISO26262是从IEC61508衍生出来的适用于汽车制造领域的功能安全标准。其中的Part.6-9[软件程序单元测试]包括了关于软件程序的构造覆盖率测试以及有关的规定项目。根据汽车安全标准(ASIL),提出了测试语句覆盖率(statement coverage),分支覆盖率(branch coverage),MC/DC覆盖率的推荐性事项。其中的另一个推荐性事项是[尽可能使单元测试的环境与目标环境相同]的规定。如果在与目标环境不同的环境下进行单元测试,必须表明源代码与目标代码的差别,以及目标环境和测试环境的差别。因此,对于那些使用与目标微机不同的电脑进行编译和单元测试的其他公司的工具而言,这个要求很难满足。 还有些公司的单元测试工具虽然包括交叉编译环境及编译功能,而且也能够在与目标环境相同的环境下进行测试,但是所有的测试都需要插入测试用代码,进行再次编译,因此测试也只能在与目标环境不同的环境下实施。GAIO提供的单元测试工具Coverage master winAMS具有●采用全面支持嵌入式微机的微机化功能测试平台环境●不需要插入测试用代码直接使用目标机代码进行测试的特征,提供符合ISO26262标准要求的必须功能。GAIO提供的Coverage master winAMS是符合ISO26262标准[直接使用整装用代码实施单元测试]这一要求的业界唯一的工具。关于汽车机能安全ISO26262的对应以及认证的获得已取得第三方认证机构TUVSUD对适用于汽车机能安全ISO26262软件工具的认证2012年6月28日,「Coverage master winAMS / General」测试工具获得由德国TUVSUD第三方认证机构,在汽车机能安全规格的ISO26262软件工具方面的认证,包括日本在内亚洲地区首次获得该项认证。通过此项认证,说明本公司的单元测试工具「Coverage master winAMS / General」,以及程序分析工具「CasePlayer2」,在静态分析和单元测试领域,是符合所有安全度水准的工具,并由TUVSUD认证机构得到了保障。ISO 26262对于不同的开发用软件工具在工具置信水平(TCL),都需要开发者提供开发软件工具的认证书。此项认证适用于在工具认证当中,最为复杂的TCL3工具认证标准。因此,导入本公司的单元测试工具之后,不需要对TCL的部分进行认证,进而可以缩减手续跟时间。主要的单元测试功能采用SSTManager管理单元测试projectSSTManager是Coverage master winAMS的应用功能,用于管理单元测试project,制作测试数据(test data)。从设定测试环境开始,到报告测试结果为止,均由微机化功能测试平台(ISS)实施综合管理。采用通用便利的CSV文件管理测试数据的输入输出Coverage master winAMS不需要插入测试用代码,直接使用目标机代码进行单元测试。采用通用便利的CSV文件管理函数测试时使用的输入输出数据。测试结束后,输出的测试结果和输出的期待值也将以相同的格式显示在CSV文件之中。C0/C1覆盖率报告的自动化制作功能(标准功能)根据测试的输入输出数据自动报告相应源代码的C0/C1测试覆盖率结果。包括通过图形(viewer)显示测试数据,以及与其相应的被测试的源代码路径的功能,用于分析测试结果。作为选项功能也包括MC/DC覆盖率测试功能。MC/DC覆盖率的自动化测试功能(选项功能)作为选项功能提供MC/DC覆盖率测试功能。C0/C1覆盖率测试不需要加工即可直接使用目标机代码。然而,MC/DC覆盖率测试对于复合式的条件式,需要自动插入HookCode将复合式的条件式分解,才能对各条件式进行测试。这样就有可能导致测试用代码与目标机用代码的不同。为了验证HookCode的妥当性,在MC/DC覆盖率测试的同时,运行目标机代码,确认运行结果与期待值的一致性。注:右图举例显示,第2个if句的复合条件式中,[gbc>30]为false时的分支没有被测试到。以C1覆盖率测试来说,它的测试结果是OK;而对于MC/DC覆盖率测试来说,它的结果是NG。注: MC/DC覆盖率测试功能不支持C++程序。单元测试的效率化功能联合程序解析工具CasePlayer2,实现代码参照解析作业的效率化利用CasePlayer2生成的流程图表以及模块构造图(调用函数的构造图)与源代码的连接(link)功能,使单元测试用源代码的解析工作效率化。能够自动检索被测试函数的外部变量,使测试条件设定效率化联合程序解析工具CasePlayer2,自动检索被测试函数所使用的外部变量。缩短了以往必须对源代码进行搜索找出输入条件的变量所需的工作。而且,能够防止人工操作导致的类似变量指定遗漏的的错误。根据代码解析自动化制作C0,C1,MC/DC 覆盖率测试计划联合程序解析工具CasePlayer2,自动化制作符合覆盖率测试要求的条件分支if,switch,for,while等的测试数据。可以将被测试函数中含有的条件式(if以及switch等)在数据制成图形(Viewer)上列表显示。点击其中的条件,工具将自动开始检索与之相关的变量,进而从所设置的条件的境界值中自动生成覆盖率测试所需要的数据。为了达到C1/MCDC覆盖率,测试时需要对各函数的数据进行组合。利用CasePlayer2提供的解析结果,分析条件式的net构造,在重复性限制在最小限度下生成C1/MCDC覆盖率测试用数据。支持MPU CoverageMaster winAMS Supported Processor List(English)动作环境・操作PC/OS・IBM PC/AT 兼容机・Pentium(相当) 2GHz 以上的CPU・存储器 512MB 以上(推荐值)・显示器分辨率 XGA(1024*768)以上(推荐值)・Windows XP, Windows Vista, Windows 7(32bit/64bit)(※Windows 95/98/Me/NT/2000 未支持)
  • [技术交流] 单元测试
     1         单元测试的重要性1.1      一些错误的认识在实际的单元测试过程中总会有一些错误的认识左右着我们,使之成为单元测试最大的障碍,在此将其一一分析如下:它太浪费时间了,现在要赶进度,时间上根本不允许,或者随便做做应付领导。我是一个很棒的程序员,我写的代码肯定是没有问题的。做单元测试太烦了,直接集成,到时有问题在集成测试时肯定能发现的,实在不行在系统测试总该能发现吧。它仅仅是证明这些代码做了什么。对于以上错误认识的产生归根结底还是由于对单元测试的理解还是不够,没有真正认识到单元测试的重要性。1.2      测试的重要性单元测试是软件测试的基础,因此单元测试的效果会直接影响到软件的后期测试,最终在很大程度上影响到产品的质量。从如下几个方面就可以看出单元测试的重要性在何处。时间方面:如果认真的做好了单元测试,在系统集成联调时非常顺利,因此会节约很多时间,反之那些由于因为时间原因不做单元测试或随便做做的则在集成时总会遇到那些本应该在单元测试就能发现的问题,而这种问题在集成时遇到往往很难让开发人员预料到,最后在苦苦寻觅中才发现这是个很低级的错误而在悔恨自己时已经浪费了很多时间,这种时间上的浪费一点都不值得,正所谓得不偿失。 测试效果:根据以往的测试经验来看,单元测试的效果是非常明显的,首先它是测试阶段的基础,做好了单元测试,在做后期的集成测试和系统测试时就很顺利。其次在单元测试过程中能发现一些很深层次的问题,同时还会发现一些很容易发现而在集成测试和系统测试很难发现的问题。再次单元测试关注的范围也特殊,它不仅仅是证明这些代码做了什么,最重要的是代码是如何做的,是否做了它该做的事情而没有做不该做的事情。测试成本:在单元测试时某些问题就很容易发现,如果在后期的测试中发现问题所花的成本将成倍数上升。比如在单元测试时发现1个问题需要1个小时,则在集成测试时发现该问题需要2个小时,在系统测试时发现则需要3个小时,同理还有定位问题和解决问题的费用也是成倍数上升的,这就是我们要尽可能早的排除尽可能多的bug来减少后期成本的因素之一。 产品质量:单元测试的好与坏直接影响到产品的质量,可能就是由于代码中的某一个小错误就导致了整个产品的质量降低一个指标,或者导致更严重的后果,如果我们做好了单元测试这种情况是可以完全避免的。    综上所述,单元测试是构筑产品质量的基石,我们不要因为节约单元测试的时间不做单元测试或随便做而让我们在后期浪费太多的不值得的时间,我们也不愿意因为由于节约那些时间导致开发出来的整个产品失败或重来!1.3      具有的优点1. 它是一种验证行为。程序中的每一项功能都是测试来验证它的正确性,为以后的开发提供支缓。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障,这样,我们就可以更自由的对程序进行改进。2.它是一种设计行为。编写单元测试将使我们从调用者观察、思考,特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。另外还可以使编码人员在编码时产生预测试,将程序的缺陷降低到最小。3.它是一种编写文档的行为。单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。4.它具有回归性。自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。 2         单元测试的基本理论2.1      基本概念1. 单元测试:单元测试又称模块测试,属于白盒测试,是最小单位的测试。模块分为程序模块和功能模块。功能模块指实现了一个完整功能的模块(单元),一个完整的程序单元具备输入、加工和输出三个环节。而且每个程序单元都应该有正规的规格说明,使之对其输入、加工和输出的关系做出名明确的描述。2.测试驱动:驱动被测试模块正常运行起来的实体3.测试桩:代替被测模块调用的子模块的实体,该实体一般为桩函数。4. 测试覆盖:评测测试过程中已经执行的代码的多少。5.覆盖率:代码的覆盖程度,一种度量方式。针对代码的测试覆盖率有许多种度量方式,定义如下: 语句覆盖(StatementCoverage):也称为行覆盖(linEC),段覆盖(segmentcoverage)和基本块覆盖(bAS)。它度量每一个可执行语句是否被执行到了。icblockcoverageoverage判定覆盖(DecisionCoverage):也被称为分支覆盖(branchcoverage),所有边界覆盖(all-edgescoverage),基本路径覆盖(basispathcoverage),判定路径覆盖(decision-decision-path或DDPtesting)。它度量是否每个BOO型的表达式取值true和false在控制结构中都被测试到了。L 条件覆盖(ConDI):它独立的度量每一个子表达式,报告每一个子表达式的结果的true或false。这个度量和判定覆盖(decisioncoverage)相似,但是对控制流更敏感。不过,完全的条件覆盖并不能保证完全的判定覆盖。tionCoverage 路径覆盖(PathCoverage):也称为断言覆盖(prEDI),它度量了是否函数的每一个可能的分支都被执行了。路径覆盖的一个好处是:需要彻底的测试。但有两个缺点:一是,路径是以分支的指数级别增加的,例如:一个函数包含10个IF语句,就有1024个路径要测试。如果加入一个IF语句,路径数就达到2048;二是,许多路径不可能与执行的数据无关。catecoverage循环覆盖(LOOP):这个度量报告你是否执行了每个循环体零次、只有一次还是多余一次(连续地)。对于do-while循环,循环覆盖报告你是否执行了每个循环体只有一次还是多余一次(连续地)。这个度量的有价值的方面是确定是否对于while循环和for循环执行了多于一次,这个信息在其它的覆盖率报告中是没有的。 2.2      测试的内容单元测试的对象是软件设计的最小单位——模块或函数,单元测试的依据是详细设描述。测试者要根据详细设计说明书和源程序清单,了解模块的I/O条件和模块的逻辑结构。主要采用白盒测试的测试用例,辅之以黑盒测试的测试用例,使之对任何合理和不合理的输入都能鉴别和响应。要求对所有的局部和全局的数据结构、外部接口和程序代码的关键部分进行桌面检查和代码审查。在单元测试中,需要对下面5个方面的内容进行测试,也是构造测试用例的基础。1)   模块接口:测试模块的数据流。如果数据不能正确地输入和输出,就谈不上进行其他测试。因此,对于模块接口需要如下的测试项目:调用所测模块时的输入参数与模块的形式参数在个数、属性、顺序上是否匹配;所测模块调用子模块时,它输入个子模块的参数与子模块的形式参数在个数、属性、顺序上是否匹配;是否修改了只做输入用的形式参数;输出给标准函数的参数在个数、属性、顺序上是否匹配;全局变量的定义在各模块中是否一致; 限制是否通过形式参数来传送。2)  局部数据结构测试:模块的局部数据结构是最常见的错误来源,应设计测试用例以检查以下各种错误: 检查不正确或不一致的数据类型说明;使用尚未赋值或尚未初始化的变量; 错误的初始值或错误的默认值;变量名拼写错误或书写错误; 不一致的数据类型。3)路径测试:对基本执行路径和循环进行测试会发现大量的错误。根据白盒测试和黑盒测试用例设计方法设计测试用例。设计测试用例查找由于错误的计算、不正确的比较或不正常的控制流而导致的错误。常见的不正确的计算有:Ø         运算的优先次序不正确或误解了运算的优先次序;Ø         运算的方式错误(运算的对象彼此在类型上不相容);Ø         算法错误;Ø         初始化不正确;Ø         运算精度不够;Ø         表达式的符号表示不正确等。 常见的比较和控制流错误有:Ø         不同数据类型的比较;Ø         不正确的逻辑运算符或优先次序;Ø         因浮点运算精度问题而造成的两值比较不等;Ø         关系表达式中不正确的变量和比较符;Ø         “差1错”,即不正确地多循环或少循环一次;Ø         错误的或不可能的循环终止条件;Ø         当遇到发散的迭代时不能终止循环;Ø         不适当地修改了循环变量等。4) 错误处理测试:比较完善的模块设计要求能预见出错的条件,并设置适当的出错处理对策,以便在程序出错时,能对出错程序重新做安排,保证其逻辑上的正确性。这种出错处理也是模块功能的一部分。表明出错处理模块有错误或缺陷的情况有:出错的描述难以理解;出错的描述不足以对错误定位和确定出错的原因;显示的错误与实际的错误不符;对错误条件的处理不正确;在对错误进行处理之前,错误条件已经引起系统的干预; 如果出错情况不予考虑,那么检查恢复正常后模块可否正常工作。5) 边界测试:边界上出现错误上常见的。设计测试用例检查: 在n次循环的第0次、1次、n次是否有错误;运算或判断中取最大最小值时是否有错误;数据流、控制流中刚好等于、大于、小于确定的比较值时是否出现错误。 2.3      测试的环境构成何时进行单元测试?单元测试在编码阶段进行。在源程序代码编制完成、经过评审和验证、确认没有语法错误之后,就可以开始进行单元测试的测试用例设计。要利用软件设计文档,设计可以验证程序功能、找出程序错误的多个测试用例。对于每一组输入,应该有预期的正确结果。在单元测试时,如果模块不是独立的程序,需要辅助测试模块,有两种辅助模块:驱动模块(Driver):所测模块的主程序。它接收测试数据,把这些数据传递给所测试模块,最后再输出测试结果。当被测试模块能完成一定功能时,也可以不要驱动模块。 桩模块(Stub):用来代替所测模块调用的子模块。      被测试模块、驱动模块和桩模块共同构成了一个测试环境,如下图所示:3         测试方法与过程3.1      用例设计1.测试用例的组成(在单元测试中测试用例基本上由测试脚本组成)用例运行前置条件被测模块/单元所需环境(全局变量赋值或初始化实体)启动测试驱动设置桩调用被测模块设置预期输出条件判断2.测试用例的设计原则一个好的测试用例在于能够发现至今没有发现的错误;测试用例应由测试输入数据和与之对应的预期输出结果这两部分组成;在测试用例设计时,应当包含合理的输入条件和不合理的输入条件;为系统运行起来而设计测试用例;为正向测试而设计测试用例;为逆向测试而设计测试用例;为满足特殊需求而设计测试用例;为代码覆盖而设计测试用例;3.用例设计方法1)        规范(规格)导出发2)        等价类划分法3)        边界值分析法4)        状态转移测试法5)        分支测试法6)        条件测试法7)        数据定义-使用测试法(又名数据流测试法)8)        内部边界值测试法9)        错误猜测法4. 特定的用例测试设计1)声明测试:检查模块中的所有变量是否被声明。经验表明,大量重要的错误都是由于变量没有被声明或没有被正确的声明而引起的。2)路径测试:要求模块中所有可能的路径都被执行一遍,属逻辑覆盖测试。基本路径测试:由于实际中,一个模块中的路径可能非常多,由于时间和资源有限,不可能一一测试到。这就需要把测试所有可能路径的目标减少到测试足够多的路径,以获得对模块的信心。要测试的最小路径集就是基本测试路径集。基本测试路径集要保证:每个确定语句的每一个方向要测试到;每条语句最少执行一次。3)循环测试:重点检查循环的条件-判断部分以及边界条件。测试循环是一种特殊的路径测试,因为循环比其他语句都复杂一些。循环中错误的发生机会比其他代码构成部分多。因此,对于任何给定的循环测试应该包括测试下面每一条件的测试用例: 循环不执行; 执行一次循环;执行两次循环;反映执行典型的循环的执行次数;如果有最大循环次数,最大循环次数减1;最大循环次数;大于最大循环次数。对于增量和减量不是1的FOR语句,要特别注意,因为程序员习惯于增量1。4) 循环嵌套:循环嵌套使逻辑的次数呈几何级数增长,设计测试嵌套循环的测试用例应该包括的测试条件有:把外循环设置为最小值,并运行内循环所有可能的情况;把内循环设置为最小值,并运行外循环所有可能的情况;把所有的循环变量都设置为最小值运行;把所有的循环变量都设置为最大值运行; 把外循环设置为最大值,并运行内循环所有可能的情况;把内循环设置为最大值,并运行外循环所有可能的情况;5) 边界值测试:指程序内部边界测试。检查确定代码在任何边界情况下都不会出差错。重点检查小于、等于和大于边界条件的情况。边界值测试是指专门设计用来测试当条件语句中引用的值处在边界或边界附近时系统反映的测试。被测试语句的最好的例子就是“IF-THEN…ELSE-ENDIF”部分。这样语句的例子如:IF a <= 123 THENb = 1ELSE IF a >= 123 THENb = 2ELSE b = 3END IF           上面例子中的边界值测试用例应该至少包括a的以下值:122,123,124。当a=123时,b=1还是2。(找出逻辑判断的矛盾)6)接口测试:检查模块的数据流(输入、输出)是否正确。检查输入的参数和声明的自变量的个数,数据类型和输入顺序是否一致。检查全局变量是否被正确的定义和使用等。7)确认测试:是否接受有效输入数据(操作),拒绝无效数据(操作)。8)事务测试:输入->输出,错误处理。3.2      用例执行一般来说,做单元测试均采用的是商用的测试工具或自行开发的测试工具,用例的编写都是在测试工具上完成,测试用例都是一些测试脚本,都以文件的方式来保存,故其用例的执行过程主要是由测试工具根据所编写的具体的测试用例脚本来完成,这样对于用例的管理和执行也非常灵活。在特定场合,比如某种压力测试或极限测试,对于测试执行过程时间很长时(几个小时以上),一般都预先编写好用例(确保用例无误),使用空闲机或非工作时间执行测试用例,这样操作起来较节约时间。在用例的执行过程中务必注意如下事项: 程序的执行过程―――便于构造发散用例不要放过任何细节―――这种细节可能就是问题 3.3      测试优化和策略在测试的过程中为了提高测试效率和效果,不断的减少冗余劳动,也为后期的回归测试和测试管理带来很大的方便,不至于感到测试很混乱无序。因此我们要对测试用例和测试执行进行不断的优化,以测试策略为指导方针进行测试。1、测试用例的优化    测试用例的优化主要是指用例的合并、修改和删除,减少冗余的无价值的测试,其优化依据来源于测试后的测试数据分析和评估,其中测试覆盖也是用例优化的主要参考。2、测试执行的优化        测试执行的优化主要是指测试步骤的优化,减少测试人员的手工操作,因为太多的手工操作会导致测试人员很厌倦,直接影响测试效果,优化依据来源于测试总结。3、测试策略    在测试过程中由于时间或资源的原因可能会使测试处于紧张的局面,在此情况下我们要采取一定的策略来解决此局面。策略来源于测试数据的分析,主要的方法是:为各模块制定测试优先级,其优先级的划分依据如下:哪些是重点模块? 哪些程序是最复杂、最容易出错的? 哪些程序是相对独立,应当提前测试的? 哪些程序最容易扩散错误?哪些程序是开发者最没有信心的? 80-20原则:80%的缺陷聚集在20%的模块中,经常出错的模块改错后还会经常出错,这种应该列入测试重点。 3.4      测试评估   单元测试完成以后,需要对单元测试的执行效果进行评估,主要从以下几方面进行:1)测试完备性评估,主要检查测试过程中是否已经执行了所有的测试用例,对新增的测试用例是否已及时更新测试方案等。2)代码覆盖率评估,主要是根据代码覆盖率工具提供的语句覆盖情况报告,检查是否达到方案中的要求,公司要求语句覆盖达到100%。但很多情况下,第一轮测试用例执行完后是很难达到的,这时在评估过程中要对覆盖率进行分析,主要从以下方面来考虑:不可能的路径或条件不可达的或冗余的代码不充分的测试用例3) 从覆盖的角度看,测试应该覆盖: 功能覆盖 输入域覆盖输出域覆盖函数交互覆盖代码执行覆盖   大多数有效的测试用例都来自于分析,而不是仅仅为了达到测试覆盖率目标而草率设计测试用例。千万不要误解测试覆盖,测试覆盖并不是我们最求的目的,它只是评价测试的一种方式,为测试提供指导和依据。3.5      测试过程1.测试过程中各种人员的作用系统分析设计人员     进行需求跟踪,确保系统需求的实现和更新。进行软件单元可测性分析,确定单元测试的对象、范围和方法。软件开发人员     负责编码和单元测试过程,完成单元测试计划、方案和报告。软件测试人员     参与单元测试计划、方案和报告的评审,对单元测试的计划、设计和执行质量进行监控。根据实际情况,可选择参与由开发人员负责的代码检视、单元测试等活动。 配置管理人员    对代码及单元测试文档进行配置管理。质量保证(QA)人员     参与编码与单元测试评审,对编码和单元测试过程进行审计。2.  单元测试输入《软件需求规格说明书》《软件详细设计说明书》《软件编码与单元测试工作任务书》《软件集成测试计划》《软件集成测试方案》用户文档3.单元测试的输出《单元测试计划》《单元测试方案》《需求跟踪说明书》或需求跟踪记录 代码静态检查记录《正规检视报告》 问题记录 问题跟踪和解决记录软件代码开发版本《单元测试报告》《软件编码与单元测试任务总结报告》3.6      测试实施1.  单元测试实施步骤1)        制定测试计划和测试方案(包括测试工具的选择)2)        根据计划和方案及相关输入文档编写测试用例3)        搭建测试环境4)        执行测试5)        记录和跟踪问题6)        编写测试报告和总结报告2.  单元测试实施遵循的原则 精心制定测试计划 严格评审测试计划 严格执行测试计划系统分析测试结果并提交报告4         常用测试工具介绍常用的C语言单元测试工具介绍如下: winAMS、CasePlayer21)        简介GAIO公司的覆盖率专家winAMS获得机能安全标百准ISO26262/IEC61508工具认证,是日本工业制造度领域普遍使用的针对C/C++的单元/集成测试工具。winAMS将通过交叉编译生成的原始代码作为评价代码,具有使用芯片仿真器进行仿真功能的测试工具。不仅可以对C/C++语言编写的程序进行逻辑水平的测试,还可以对嵌入式软件特有的依存于芯片的问题点进行确认。2)        功能特性尽可能使单元测试的环境与目标环境相同采用全面支持嵌入式微机的微机化功能测试平台环境不需要插入测试用代码直接使用目标机代码进行测试取得第三方认证机构TUVSUD对适用于汽车机能安全ISO26262软件工具的认证测试工程管理
  • [数据库] MySQL-单元测试介绍
    1.1 MySQL单元测试介绍 MySQL有两套单元测试工具,一个是tap,一个是gtest,现在用gtest。 tap:#include "unittest/mytap/tap.h" gtest: 1.2 目录结构单元测试主目录:unittest examples:tap参考用例目录gunit:gtest用例目录mytap:tap工具目录 gunit目录: 1.3 编译编译时要加上这个参数:-DENABLE_DOWNLOADS=1会自动下载gtest工具。 如果无法自动下载,自己下载googletest-release-1.8.1.zip放到source_downloads目录下,无需解压。 修改了原代码可能会导致编译不通过,需要修改单元测试的CMakeLists.txt或代码。 如何判断编译是否成功,进入unittest目录执行ctest命令1.4 测试方法单元测试要在build目录下执行,不能在install目录下执行。1.4.1 全量测试build主目录下执行ctest 1.4.2 执行gtest用例unittest/gunit下执行ctest 或 make test 1.4.3 执行单个测试集或单个用例文件runtime_output_directory1.4.4 执行单个用例 -h 查看帮助信息1.5 添加单元测试用例文件名要加-t ,没有加-t的文件编译成库文件供用例链接。  1.6 用例编写1.6.1 gtest使用略过 1.6.2 gmock使用包含头文件 使用步骤:定义一个Mock类: 创建一个Mock对象 定义Mock对象的行为EXPECT_CALL 执行测试代码 
  • [问题求助] 【SDC D系列产品】【分类网络功能】单元测试 和 集成运行时输出结果不一致
    在SDC D2150上训练好的分类网络:单元测试结果  和 集成测试结果不一致集成测试: 通过灌流 通过“分类网络”(使用nnie)计算后,得到每一个图片和分类得到的分数。单元测试:将集成测试得到的所有图片,每次一张图片的进行“分类网络”计算后,得到分类的分数。【测试结果】1. 集成测试 和 单元测试 结果不一致,差异很大;2. 单元测试结果(计算得到的分数)是正确的(或正常的);3. 集成测试结果中,有部分结果异常(应该低分的得到高分,应该高分的得到低分);【请问】1. 可能什么原因导致以上的结果?2. 在 图片数据 传给sdc  nnie时,如何确定nnie接收到的数据是传入进去的?如何确保nnie完整的接收输入数据?2. 如何将传入sdc nnie的图片数据,从sdc  nnie读取出来并保存?【追加问题 20210918】1、使用nnie是否有调用频率的限制?(比如不能调用太频繁太快了,不然会导致结果异常)     在使用过程中,发现调用频率太高,会出现输入数据是对的,但调用nnie结束后,输入数据被篡改了(这里没有写错,是输入数据被篡改,不是输出数据)
  • [执行问题] 【AKG】910平台AKG单元测试出现undefined symbol的错误
    【功能模块】910平台AKG单元测试【操作步骤&问题现象】1、910平台AKG在单元测试时出现OSError: libakg.so:  undefined symbol:_ZN6Msprof6Engine21MsprofCallbackHandler10reporters_E2、这个函数应该来源于名为libmsprofiler_fwkacl.a的静态库, 使用nm命令可以在libakg.so中找到这个函数, 但是运行时却会报错, 原因在哪?【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] 上手 Jest 测试框架
      当我第一次听说TDD(Test-Driven Develop, 测试驱动开发)的时候,我首先想到的是测试小姐姐们想搞事情?后面发现这个玩意儿是想干掉测试,那好,下面开始让我们干掉这群整天给我们找bug…下面开始从简单的单元测试上手Jest测试框架。我们能学到什么Jest怎么4行代码完成一个测试用例Jest怎么让测试用例覆盖率100%Jest怎么和Typescript完美结合(填坑实录)Jest最锋利的功能 Mock Functions项目初始化创建工程# 注意powershell中'&&'替换为';'mkdir jest-just-go && cd jest-just-go初始化npm init -y安装依赖npm i jest --save-dev1.Jest怎么4行代码完成一个测试用例初始化Jest默认配置npx jest --init初始化时会出现提示信息,按y或enter即可。编写功能代码  现在让我们正式开始,茶和图雀社区精心准备的甜品更搭哦。  在项目根目录下新建src目录,存放我们的功能代码。然后创建src/dessert.js。const dessert = function (name) { this.name = name; } dessert.prototype = { enjoy: function () { return "Enjoy the " + this.name; } }module.exports = dessert;  我们已经编写了一个甜品类型,它有一个提供品尝的方法enjoy编写测试用例  下面开始编码,实现对上面甜品功能的单元测试。  在项目根目录下新建__tests__目录,存放我们的测试用例。然后创建__tests__/dessert.test.js。const dessert = require("../src/dessert"); describe("test dessert feature", () => { test("enjoy the cake", () => { const cake = new dessert('cake'); expect(cake.enjoy()).toBe("Enjoy the cake"); } }  简单的四行代码,我们的第一个测试用例就已经大功告成。这里我们只需要注意 describe、test、expect 这3个 Jest 关键字就行了:describe:组合同一类的 test 用例,可以添加 beforeEach \ afterEach、beforeAll \ afterAll (这里由于篇幅,这一类进阶特性将放在后续的教程中)为其下所有 test 进行统一描述和处理。test:描述具体的测试用例,是单元测试的最小单元。expect: Jest 最终落在了每一个对测试结果的 期望 上,通过 expect 中的返回值或是函数执行结果来和期望值进行对比。执行测试  回到控制台,输入:npm test  无需更多配置,测试结果显示如下:  其中:%Stmts 是语句覆盖率(statement coverage):是不是每个语句都执行了?%Branch 分支覆盖率(branch coverage):是不是每个if代码块都执行了?%Funcs 函数覆盖率(function coverage):是不是每个函数都调用了?%Lines 行覆盖率(line coverage):是不是每一行都执行了?  %Stmts 和 %Lines 的区别是:行覆盖率的颗粒度是大于语句覆盖率的,因为可能允许一行中有多条语句(js开发中尤为常见)。  我们更改__tests__/dessert.test.js:expect(cake.enjoy()).toBe("Enjoy the cake");  改为expect(cake.enjoy()).toBe("enjoy the cake");  执行测试命令查看测试不通过的情形:2.Jest怎么让测试用例覆盖率达到100%  当我们的功能场景逐渐变得复杂,我们的测试就必须确保测试用例的覆盖率达到一个标准。最佳当然是100%啦,这样才能保证测试小改改们找不到我们的茬,闲的没事就会主动找我们拉话话啦,美好生活从测试用例覆盖率100%开始。编写功能代码  甜点不够怎么办?要不我们开家店吧!先让红豆烧和提拉米苏够吃先~const dessert = require("./dessert"); const dessertFactory = function () { } dessertFactory.produce = function (type) { switch (type) { case 'Red bean burning': return new dessert('Red bean burning'); // 红豆烧 case 'Tiramisu': return new dessert('Tiramisu'); // 提拉米苏 default: throw new Error("please choose a dessert type"); } }module.exports = dessertFactory;  现在想吃什么通过dessertFactory.produce(...)order就fine啦~编写测试用例  不过,要保证我们想吃的时候就必须能吃到(这个很重要),我们先验收先:  __tests__/dessertFactory.test.jsconst dessertFactoty = require("../src/dessertFactoty"); describe("test dessertFactoty feature", () => { test("produce all dessert", () => { const dessertType = ['Red bean burning', 'Tiramisu']; expect(dessertFactoty.produce(dessertType[0]).enjoy()).toBe("Enjoy the Red bean burning"); } }  –“我不要你觉得,我要我觉得“,我要上档次的“验收报告“!  –行,网页展示出来怎么样配置jest.config.js保存测试用例覆盖率执行报告  我们在执初始化Jest默认配置的时候,会生成在项目根目录下生成jest.config.js,里面列出了所有的配置项,未设置的已经被注释掉了。我们要将每次执行测试后生成的覆盖率报告保存下来需要找到下面这项配置属性并更改:// Indicates whether the coverage information should be collected while executing the test collectCoverage: true,  然后我们可以再次执行测试命令并用浏览器打开_/coverage/lcov-report/index.html_查看测试用例覆盖率报告。修改测试用例使覆盖率达到100%  __tests__/dessertFactory.test.jsconst dessertFactoty = require("../src/dessertFactoty"); describe("test dessertFactoty feature", () => { test("produce all dessert", () => { const dessertType = ['Red bean burning', 'Tiramisu']; expect(dessertFactoty.produce(dessertType[0]).enjoy()).toBe("Enjoy the Red bean burning"); expect(dessertFactoty.produce(dessertType[1]).enjoy()).toBe("Enjoy the Tiramisu"); expect(() => { dessertFactoty.produce('Luckin Coffee') }).toThrow("please choose a dessert type"); } }  再测试并查看覆盖率报告:  这里 Functions列 为什么不是100%,大家可以点击 dessertFactory.js 根据详细说明分析推测。3.Jest怎么和Typescript完美结合(填坑实录)  搜索引擎上现有的 Jest + Typescript 的样例比较少,并且存在了一定的问题没有解决,这一部分我已经填平了坑,可以作为配置参考。增加依赖npm i ts-jest @types/jest typescript @types/node --save-dev  其中 ts-jest 为 Jest + Typescript 环境下进行测试提供了类型检查支持和预处理。ts初始化  在根目录下创建配置文件tsconfig.json:{ "compilerOptions": { "target": "es5", "module": "commonjs", "lib": [ "es2015" ], "strict": true, "declaration": true, "outDir": "build", "sourceMap": true }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "__test__/**/*" ] }修改jest.config.js配置  添加如下配置项:// An array of file extensions your modules use moduleFileExtensions: [ "js", "json", "jsx", "ts", "tsx", "node" ], // A preset that is used as a base for Jest's configuration preset: "ts-jest",修改功能代码  src/dessert.js => src/dessert.tsexport default class dessert { name: string; constructor(name: string) { this.name = name; } enjoy() { return "Enjoy the " + this.name; } }  src/dessertFactory.js => src/dessertFactory.tsimport dessert from "./dessert"; export default class dessertFactory { static produce(type: string) { switch (type) { case 'Red bean burning': return new dessert('Red bean burning'); // 红豆烧 case 'Tiramisu': return new dessert('Tiramisu'); // 提拉米苏 default: throw new Error("please choose a dessert type"); } } }修改测试用例  __tests__/dessert.js => __tests__/dessert.tsimport dessert from "../src/dessert"; describe("test dessert feature", () => { test("enjoy the cake", () => { const cake = new dessert('cake'); expect(cake.enjoy()).toBe("Enjoy the cake"); } }  __tests__/dessertFactory.js => __tests__/dessertFactory.tsimport dessertFactoty from "../src/dessertFactoty"; describe("test dessertFactoty feature", () => { test("produce all dessert", () => { const dessertType = ['Red bean burning', 'Tiramisu']; expect(dessertFactoty.produce(dessertType[0]).enjoy()).toBe("Enjoy the Red bean burning"); expect(dessertFactoty.produce(dessertType[1]).enjoy()).toBe("Enjoy the Tiramisu"); expect(() => { dessertFactoty.produce('Luckin Coffee') }).toThrow("please choose a dessert type"); } }  如同代码重构后我们通过测试用例可以快速检查是否改动出现差错一样,我们这次变更可以执行 Jest 测试命令,检查是否对功能无影响。4.Jest最锋利的功能 Mock Functions  关于 Jest 测试框架中的Mock功能,我们主要关注两点:mock function: 对函数进行mock.mock return value: 对返回值进行mock.  从以上两点可以衍生出 Jest 对于代码单元测试中两项常用的锋利功能:对功能中业务逻辑简化后的重新实现,方便有指向性的进行测试(比如忽略实际场景中的跨服务调用功能等,仅需将原有功能中对应的调用逻辑改为定义的测试数据即可)。对功能返回值的直接模拟。  为甜点增加了评论功能:export default class dessert { name: string; static comment: string[] = []; constructor(name: string) { this.name = name; } enjoy() { return "Enjoy the " + this.name; } static comments(message: string) { dessert.comment.push(message); } }  专门建立一个互动区进行甜点的讨论。创建src/dessertCommentModule.ts:import dessert from "./dessert"; module dessertCommentModule { export function comments(message: string) { dessert.comments(message); return dessert.comment; } }export default dessertCommentModule;  下面开始test dessert feature with mock~这一部分均已通过测试,建议深入研读代码,感受 Jest mock 每一处的奥妙。如果下面各处期望的结果都如你所料,那么你就是图雀社区最靓的仔:import dessert from "../src/dessert"; import dessertCommentModule from "../src/dessertCommentModule"; jest.mock("../src/dessertCommentModule"); describe("test dessert feature", () => { test("enjoy the cake", () => { const cake = new dessert('cake'); expect(cake.enjoy()).toBe("Enjoy the cake"); } } describe("test dessert feature with mock", () => { test("enjoy the cake with mock function", () => { const dessertFactoryMock = jest.fn(name => new dessert(name)); const cake = dessertFactoryMock('cake'); expect(cake.enjoy()).toBe("Enjoy the cake"); expect(dessertFactoryMock.mock.results[0].value.enjoy()).toBe("Enjoy the cake"); console.log(dessertFactoryMock.mock); } test("enjoy the cake with mock return value", () => { const dessertFactoryMock = jest.fn(name => new dessert(name)); const cake = new dessert('cake'); dessertFactoryMock.mockReturnValue(cake); const tiramisu = dessertFactoryMock('tiramisu'); expect(tiramisu.enjoy()).toBe("Enjoy the cake"); expect(tiramisu).toEqual(cake); expect(dessertFactoryMock.mock.results[0].value).toEqual(cake); } test("comment the dessert with mock module", () => { const mockedDessert = dessertCommentModule as jest.Mocked<typeof dessertCommentModule>; mockedDessert.comments.mockReturnValue(['not bad']); expect(mockedDessert.comments("cake is so good")).toEqual(['not bad']); expect(dessert.comment).toEqual([]); } test("comment the dessert with mock implementations", () => { const mockedDessert = dessertCommentModule as jest.Mocked<typeof dessertCommentModule>; mockedDessert.comments.mockImplementation((message: string) => { dessert.comments(message); return ['not bad']; } expect(mockedDessert.comments("cake is so good")).toEqual(['not bad']); expect(dessert.comment).toEqual(['cake is so good']); } } mock function注意:我们可以更改test为test.only仅对test.only的测试用例执行测试,降低测试时间并关注特定测试点。test.only("enjoy the cake with mock function", () => { ...  这里我们通过 jest.fn 进行 了 mock function 功能的展示,通过执行 npm test 看到 .mock 的结构信息:.mock的中将会记录mock function调用后的相关信息。mock return value test("enjoy the cake with mock return value", () => { ....这里我们通过 .mockReturnValu 可以将 function mock 的操作略过,直接会返回 .mockReturnValue 中填充的返回值。通过执行 npm test 验证。mock module import dessertCommentModule from "../src/dessertCommentModule"; jest.mock("../src/dessertCommentModule"); test.only("comment the dessert with mock module", () => { ...通过 jest.mock ,我们 mock 了甜点评论区,这项操作可以使我们对dessertCommentModule中的所有功能进行我们的测试定制。这里我们通过对评论功能进行 mock return value ,通过执行 npm test 看到:并没有进入dessertCommentModule中的comments方法,直接返回了我们预置的返回值。mock implementations test.only("comment the dessert with mock implementations", () => { ...我们通过对评论功能进行 mock implementations ,通过执行 npm test 看到:  进入了 mockImplementation 中的测试定制功能,并且调用了dessert中的comments方法。      以上。————————————————
  • [问题求助] 初学算子,执行文件代码单元测试用例跑不通,提示“RuntimeError: Expect out tensor is None.
    初学算子开发,用MindStudio2的生成框架学着改写了一个算子的测试用例,就几行代码,总是跑不通,提示“RuntimeError: Expect out tensor is None.算子执行代码文件、单元测试用例文件请见附件。请各位学长帮着诊断、指导一下,先谢谢啦【单元测试用例代码】# # -*- coding:utf-8 -*-import sysfrom op_test_frame.ut import OpUTfrom impl.h_swish import h_swishut_case = OpUT("h_swish")# [TODO] coding expect function heredef calc_expect_func(input_x, output_y):    res = h_swish(input_x, output_y)    return [res, ]# [TODO] coding cases hereut_case.add_precision_case(case={    "params": [        {            "shape": (32, 64),            "ori_shape": (32, 64),            "format": "ND",            "ori_format": "ND",            "dtype": "float16",            "param_type": "input",            "value_range": [2.0, 3.0]        },        {            "shape": (32, 64),            "ori_shape": (32, 64),            "format": "ND",            "ori_format": "ND",            "dtype": "float16",            "param_type": "output"        }    ],    "calc_expect_func": calc_expect_func})if __name__ == '__main__':    ut_case.run("Ascend910", None, "ca", None)【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] eggjs框架学习心得
                    egg-project├── .autod.conf.js【eggjs调用的配置文件】├── .travis.yml 【travis配置文件】├── appveyor.yml【appveyor配置文件】├── .eslintignore 【忽略代码格式化配置文件】├── .eslintrc 【代码格式化配置文件】├── package.json【相关依赖模板、版本信息、启动命令配置文件】├── package_lock.json【node_modules目录下所有模块的具体来源和版本号以及其他的信息】├── jsconfig.json【VSCODE指定根文件和JavaScript语言服务提供的功能选项】├── app.js (可选) 【自定义启动时的初始化工作】├── agent.js (可选) 【自定义agent启动时的初始化工作】├── logs 【日志目录】├── node_modules【node模块目录】├── run【运行时生成的配置文件目录】├── typings 【智能提示控件】├── app 【应用层目录】|   ├── router.js 【配置 URL 路由规则文件】│   ├── controller 【控制器目录】│   |   └── home.js 【用于解析用户的输入,处理后返回相应的结果的文件】│   ├── service (可选)【服务层目录】│   |   └── user.js 【编写业务逻辑层文件】│   ├── middleware (可选) 【中间件目录】│   |   └── response_time.js 【中间件文件】│   ├── schedule (可选)【定时任务目录】│   |   └── my_task.js 【定时任务文件】│   ├── public (可选) 【静态资源文件】│   |   └── reset.css 【一个css文件】│   ├── view (可选) 【模板文件目录】│   |   └── home.tpl 【模板文件】│   └── extend (可选) 【框架的扩展目录】│       ├── helper.js (可选)【扩展helper对象文件】│       ├── request.js (可选)【扩展request对象文件】│       ├── response.js (可选)【扩展response对象文件】│       ├── context.js (可选) 【扩展ctx对象文件】│       ├── application.js (可选) 【扩展app对象文件】│       └── agent.js (可选)【扩展agent对象文件】├── config 【配置文件目录】|   ├── plugin.js 【插件的配置文件】|   ├── config.default.js 【默认的配置文件】│   ├── config.prod.js 【prod环境加载的配置文件】|   ├── config.test.js (可选)【test环境加载的配置文件】|   ├── config.local.js (可选)【local环境加载的配置文件】|   └── config.unittest.js (可选)【unittest环境加载的配置文件】└── test 【单元测试目录】    ├── middleware 【中间件单元测试目录】    |   └── response_time.test.js 【单元测试文件】    └── controller 【控制器单元测试目录】        └── home.test.js  【单元测试文件】      
  • 白盒测试工具推荐
    1.jtest java语言的单元测试框架2.JUnit 验证java的工具3.cppunit 跨平台的c++单元测试框架4.gtest 跨平台的c++单元测试框架5.BoundsChecker C++,Delphi API和OLE错误检查、指针和泄露错误检查、内存错误检查6.TrueTime C++,Java,Visual Basic 代码运行效率检查、组件性能的分析
  • 白盒测试工具
    1,jtest java语言的单元测试框架2,JUnit 验证java的工具3,cppunit 跨平台的c++单元测试框架4,gtest 跨平台的c++单元测试框架5,PhpUnit Php6,BoundsChecker C++,Delphi API和OLE错误检查、指针和泄露错误检查、内存错误检查7,TrueTime C++,Java,Visual Basic 代码运行效率检查、组件性能的分析
  • [MindX SDK] 一键式运维数据收集工具
    一、工具介绍一键式信息收集工具用于快速收集MindX SDK问题,定位所依赖的相关信息。主要包括芯片信息(版本和日志)、操作系统版本信息、环境变量、网络信息以及MindXSDK信息(版本、配置文件、日志、第三方库版本)等。 避免让客户反复收集日志,反复确认信息。该工具由sdk_info_collector.sh和sdk_info_collector.py两个文件组成,其中在sdk_info_collector.sh文件中调用了sdk_info_collector.py文件。此外,用户可以根据自己的需求在已有的信息收集工具上扩展新功能。一键式信息收集工具为芯片日志和MindX SDK日志的信息收集提供了可配置的参数,请参见下表,其他的信息都默认收集。二、工具使用步骤1 跳转到信息收集工具所在的目录。cd ${MX_SDK_HOME}/bin注意:请确保MX_SDK_HOME为有效的环境变量。步骤2 执行sdk_info_collector.sh。以下为三种一键式信息采集的方式,用户可以根据自己的实际情况来使用。采用默认参数。bash sdk_info_collector.sh -p logs_path采用默认参数收集信息,默认收集MindX SDK所有日志信息,不收集芯片日志。指定参数收集。命令示例:bash sdk_info_collector.sh -p logs_path -r 3d -t w -x 6命令解释如下表所示。命令示例:bash sdk_info_collector.sh -p logs_path -r 3d -t w -x 0,2,5全量收集。bash sdk_info_collector.sh -p logs_path -r full -t full -x -1步骤3 获取结果。运行一键式信息收集工具的结果存放在“MX_SDK_HOME/bin”目录下的mindx_sdk_info_年月日_时分秒.tar.gz文件。
  • [技术干货] python中的测试框架
    一、测试常用规则一个测试单元必须关注一个很小的功能函数,证明它是正确的;每个测试单元必须是完全独立的,必须能单独运行。这样意味着每一个测试方法必须重新加载数据,执行完毕后做一些清理工作。通常通过setUp()和setDown()方法处理;编写执行快速的测试代码。在某些情况下,测试需要加载复杂的数据结构,而且每次执行的时候都要重新加载,这个时候测试执行会很慢。因此,在这种情况下,可以将这种测试放置一个后台的任务中。在编写代码前执行完整的测试,而且在编写代码后再重新执行一次。这样能保证你后来编写的代码不会破坏任何事情;在提交代码前执行完整的测试;如果在开发期间被打断了工作,写一个打断的单元测试,关于你下一步将要开发的。当你回来工作时,你能知道上一步开发到的指针;单元测试函数使用长的而且具有描述性的名字。在正式执行代码中,可能使用square()或sqr()取名,但是在测试函数中,你必须取像test_square_of_number_2()、test_square_negativer_number()这些名字,这些名字描述更加清楚;测试代码必须具有可读性;单元测试对新进的开发人员来说是工作指南。二、python常用的测试框架1. unittestunittest是Python内置的标准类库unittest 和 JUnit类似,可以说是python的标准单元测试框架,所以有时也被人称为 PyUnit。它使用起来和xUnit 家族其他成员类似。 用的人也比较多。兼容 python2 以及python3 。2、unittest2unittest2 可以说是一个针对 unittest测试框架新特性的补丁。它很大程度上和unittest都类似。然后还添加了一些unittest没有的方法。3、pytestpy.test是unittest的替代工具。尽管它是一个功能丰富、灵活的测试框架,但是它的语法很简单。创建一个单元测试就像编写一个模块一样。相比unittest,实现相同的测试功能,py.test做的事情更少。pytest 直接可以通过 @pytest.mark.parametrize 进行参数化,而unittest 则需要借助DDT。4、noseNose是对unittest的扩展,使得python的测试更加简单。nose自动发现测试代码并执行,nose提供了大量的插件,比如测试输出的xUnitcompatible,覆盖报表等等。基于Python的测试驱动开发实战 也有nose的用法: http://python.jobbole.com/81305/还有一个特定就是,nose可以采用 @with_setup() 来定义方法的setup和teardown。5、doctestdoctest模块会搜索那些看起来像交互式会话的 Python 代码片段,然后尝试执行并验证结果。6、tox最大的特色,是自动最测试环境的管理以及使用多个解析器配置进行测试。tox的详细文档: http://testrun.org/tox/latest/7、mockunittest.mock是用来测试python的库。在python3.3版本以后,这个是一个标准库。 对老版本来说,使用pip install mock 进行安装。mock的精髓在于,你可以使用模拟的对象来替代你的系统的一部分,然后验证后续的执行是否正确。mock的详细文档:http://www.voidspace.org.uk/python/mock/
  • [AI人工智能全栈成长...] 【问答官】深度学习代码如何进行单元测试
    深度学习代码如何进行单元测试
  • [分享交流] Wings企业级单元测试自动编码引擎白皮书
    第一章Wings企业级单元测试自动编码引擎诞生的背景 (附Wings讲解视频和白皮书pdf下载)随着科技的飞速发展,软件系统越来越复杂,在系统测试阶段不断遇到的瓶颈,迫使行业逐步追根溯源到了单元测试阶段。软件缺陷发现得越晚,其处理费用就越呈几何激增,因此测试左移概念已经成为趋势。单元测试面临的最大问题是单元测试用例编写工作量巨大,极端情况下与开发工作量比达到1:1,甚至更高,这使大部分开发团队要么主动忽视单元测试,要么象征性的走个流程。如果可以让计算机先对被测试程序进行全局分析和深度理解,再由计算机进行全自动的完成单元测试编码,同时还能确保自动编写的代码无语法、语义错误的直接运行起来,这种用计算机智能算法全自动产生的测试编码去验证开发人员编写的源代码逻辑输入输出对错的高端测试模式,无疑是未来软件测试领域最为璀璨的“明珠”技术。国外软件诸如c++ test完成了这个领域的初步技术探索,星云测试研发的Wings(目前商用产品支持c/c++程序)产品,则大踏步完成了整体技术跨越和多方商用落地验证。Wings可以对程序参数进行深度解析,比如c++类、模板类、数组、结构体、指针、链表以及任意复杂结构的层级嵌套,同时对于面向对象的程序特性以及常用的容器库,能够完美识别和支持。对于一些void*、函数指针、模板类等无法直接静态分析进行类型确定的特殊情况,均有基于人工智能的程序分析辅助进行类型确定。Wings在基于深度参数解析的基础上,对于全局范围的程序进行理解分析后,第一步 按照内置规则,自动化构建被测程序的输入用例代码;第二步 构建测试代码用于调用被测程序的源代码;第三步 构建被测程序输出断言,完成调用被测试程序单元的全部环境准备。这个构建速度非常快,可以达到每分钟100万行左右的生成速度,编写的代码比程序开发人员手工编写的规范度高出一截,并确保100% 的语法语义正确,免去大量的调试时间。在驱动数据上,Wings实现了驱动代码和数据的分离。Wings基于深度参数解析基础上,可以根据参数的结构自动生成层级嵌套的测试数据结构,用图形界面可视化的展示给用户。用户只需要根据Wings提供的界面向导对测试数据进行填充即可,驱动程序会自动识别并读取这些数据,完成对被测试程序的调用。Wings还可以全自动生成参数捕获程序,并自动插装在被测试程序中。当被测试程序运行后,可以通过专用软件捕获程序中每个函数模块运行的具体参数值。Wings的测试代码驱动自动生成和参数捕获,相当于完成了一种全智能的闭环测试验证体系。Wings使测试数据不需要人工准备,只需要在前序轮次中通过参数捕获自动存储。若前序测试用例运行正常,那么这些数据都可以作为后续测试输入和进行校验的基础数据。第二章 单元测试自动生成技术2.1 测试左移后单元测试面临的问题测试左移后,传统的单元测试一般面临很多问题,主要如下:(1) 传统程序级测试用例的编写会耗费开发人员大量的工时,比如TDD测试驱动开发里面的单元测试无法有效实施,导致所有测试几乎全部依赖于系统级黑盒测试。程序级测试用例的开发成本是功能实现代码本身时间至少为1:1,绝大部分企业选择放弃开发程序级测试,而采用系统级测试方法。(2)     需求发生变化导致程序实现发生变化后,程序集用例也需要发生变化,和自动化面临的问题一样,单元测试本身的可维护性问题导致投入是持续的而不是一次性的,会打消其企业应用的热情。(3)     很多单元在未组装成系统的时候切入,如果需要进行测试需要进行大量的mock操作或者桩模拟,这个过程会造成单元测试的不精确性。(4)     程序集测试数据量很大,全部需要用户来进行准备无法全自动从前序系统测试过程中获取。      针对以上问题,很多业内人士提出了很多办法,例如自动生成测试用例、自动构建测试驱动、模糊测试方法等诸多方式,但是实际开发中程序输入参数十分复杂,简单的输入已经不能满足,如何能够构建复杂的参数输入,是要解决的重要问题。因此,星云测试研发了Wings产品--单元级的自动编码引擎,它不仅能够自动构建测试驱动,还能处理十分复杂的大型程序参数,帮助企业能够更好的进行单元测试。2.2 完成单元测试要素构成单元测试的要素如下:a.        测试数据b.        测试驱动代码例如针对以下程序,需要准备的测试输入以及测试代码① 全局变量:c② 参数:a、b③ 预期输出:320 30 int c = 100; int sum(int a, int b) {      return a + b + c; } int driver_sum() {      int a = 100, b = 20;      c = 200;      return sum(a, b); }                                                                                TEST(test, driver_sum) {      EXPECT_EQ(320, driver_sum);      EXPECT_EQ(30, driver_sum); }2.3构建测试数据和测试代码遇到的技术瓶颈编写测试代码比较繁琐开发编写驱动不难,但是浪费大量的时间,并且没有创造性。编写代码是否有良好的代码规范单元测试也需要一些规范支持,例如代码规范,注释规范等,有了这些规范能够为测试人员提供很好的功能测试用例设计的逻辑思考,也为其他开发熟悉代码提供极大的便利。数据类型比较复杂通常情况下,输入参数比较复杂,嵌套层析结构比较深入,例如类包含其他类等。能否支持多组测试数据一般代码中每个循环、分支判断以及输入输出都有可能产生缺陷。因此单元测试需要很多用例,满足不同的条件分支,达到满足覆盖率。第三章 Wings的基本架构介绍3.1 Wings测试用例驱动自动生成技术的特性l  Wings是智能的全自动测试用例驱动构建系统,能够在很短时间内完成对大型复杂程序的自动解析、构建。每分钟达到100万行代码的生成速率。l  可以将任意复杂类型逐步分解为基本数据类型,例如结构体嵌套,复杂类对象,c++标准容器,自定义模板类等。l  支持多层次的可视化的数据表格来对变量类型进行赋值,无需关注驱动程序本身。数据表格可以表达任意深度和多层次的数据关系,用户只需要关注表格数据,完成对输入数据的校对。l  能够区分系统数据类型和用户自定义类型,对于复杂的约定俗成系统类型可由用户自定义扁平式赋值模板,例如std::vector类型等,内部集成常用系统类型的模板。3.2 Wings的技术架构介绍Wings的技术架构:首先Wings利用代码静态分析技术,提取被测程序的主干信息并保存到Program Structure Description(PSD)结构中,PSD结构中主要存储函数的参数、全局变量以及返回值的信息,类的成员变量,成员函数,以及访问权限等信息。利用存储的信息,依据一定的编写规则,自动构建测试驱动、googletest 期望断言、测试输入、参数捕获等代码和值。                                             图 3.2 Wings总体架构图上述Wings的总体架构图,说明了Wings构建代码与测试输入的具体过程,整个核心技术是通过编译底层技术获取程序的信息,然后按照一定的规则构建需要的数据。第四章 程序结构信息描述程序结构信息(Program Structure Description)是指对源程序进行提取后的描述信息,主要包括类名、命名空间、类的成员变量信息、类的函数信息,以及各种类型信息等(程序结构信息,下文简称“PSD”)。描述信息的保存C语言是以一个文件为单元存储,C++提取所有的类信息存储在一个文件中。Wings的主要特性是能够对复杂的类型(结构体、类、模板类等)进行逐层展开分解到最基本数据类型(char、int、string等)。PSD结构存储在XML文件中,不同的XML文件存储不同的描述信息。Wings提取的程序结果都存储在Wingsprojects文件下的funxml与globalxml文件夹中,其中funxml文件中主要存储的文件以及作用如下:RecordDecl.xml: 结构体,联合体与类描述信息。ClassTemplateDecl.xml:模板类描述信息EnumDecl.xml:枚举描述信息funcPoint.txt: 存储函数参数为函数指针的分析结果。funcCount.txt: 存储分析到的全部函数,参数个数,参数类型信息。void.txt: 存储函数参数为void*的分析结果。filename.xml:存储c语言文件的信息。注:具体文件的描述信息,参看附录A。针对复杂类型,例如结构体类型location_s,成员变量中除了基本数据类型之外,还存在包含结构体类型的情况,如下所示的代码中,location_s中包含coordinate_s结构体,以及FILE等类型的信息,针对不同的类型进行标记区分。 源代码如下:typedef struct coordinate_s{     void(*setx)(double, int);     int mInt;     char *mPoi;     int mArr[2][3];     void *vo; } coordinate;   typedef struct location_s {     int **mPoi;     coordinate *coor;     FILE *pf;     struct location_s *next; }location;   coordinate *coordinate_create(void); int coordinate_destroy(location *loc,size_t length,char *ch); void func_point(double param1, int param2); 生成对应的PSD结构信息如下图4:图4:PSD描述信息C++的主要表示类型是类,因此测试是C++以一个类为单元做测试,类主要包括类的成员变量名以及类型信息,成员变量的访问权限信息。类的成员函数分为构造函数、内联函数、虚函数等,成员函数的参数信息以及类型信息等。具体描述信息可以登陆www.codeWings.net下载Wings试用版本进行学习。第五章 Wings构建程序描述Wings通过获取到的存储信息,依据一定的编码规则,构建不同的代码程序,如驱动程序、期望断言程序、参数捕获程序等。5.1 驱动程序构建驱动程序指单元测试代码,Wings针对函数参数的类型,自动完成单元测试代码的编写。为了更详细的说明驱动程序,以一个C++类作为具体的例子说明。 类的声明如下:class BlockBuilder { public:     explicit BlockBuilder(const Options* options);     BlockBuilder(const BlockBuilder&) = delete;     BlockBuilder& operator=(const BlockBuilder&) = delete;     void Reset();     void Add(const Slice& key, std::string & value);     Slice Finish();     size_t CurrentSizeEstimate() const; private:     const Options* options_;     std::string buffer_;     int counter_;     bool finished_; };针对如上BlockBuilder 类,构建一个对应的驱动类DriverBlockBuilder,驱动类中主要包含构造函数、析构函数、每个成员函数的驱动函数以及返回值函数。为了避免类中重名函数,对写入PSD需要对应生成驱动的函数进行顺序编号。驱动类声明:class DriverBlockBuilder { public:     DriverBlockBuilder(Json::Value Root, int times);     ~DriverBlockBuilder();     int DriverBlockBuilderReset1(int times);     int Reset1Times;     int DriverBlockBuilderAdd2(int times);     int Add2Times;     int DriverBlockBuilderFinish3(int times);     void ReturnDriver_Finish3(class Wings::Slice returnType);     int Finish3Times;     int DriverBlockBuilderCurrentSizeEstimate4(int times);     void ReturnDriver_CurrentSizeEstimate4(size_t returnType);     int CurrentSizeEstimate4Times; private:     Wings::BlockBuilder* _BlockBuilder; };在上述驱动类中,构造函数的作用是构造BlockBuilder的对象,用构造的对象,调用测试BlockBuilder中的成员函数。析构函数的作用是释放构建的对象。 构造函数与析构函数:DriverBlockBuilder::DriverBlockBuilder(Json::Value Root, int times){   Json::Value _BlockBuilder_Root = Root["BlockBuilder" + std::to_string(times)];   /* options_ */   Json::Value _options__Root = _BlockBuilder_Root["options_"];   int _options__len = _options__Root.size();   Wings::Options* _options_ = DriverstructOptionsPoint(_options__Root, _options__len);   /* buffer_ */   std::string _buffer_= _BlockBuilder_Root["buffer_"].asString();   /* counter_ */   int _counter_ = _BlockBuilder_Root["counter_"].asInt();   /* finished_ */   bool _finished_;   int _finished__value_ = _BlockBuilder_Root["finished_"].asInt();   if (_finished__value_ == 0) {     _finished_ = true;    }   else {     _finished_ = false;   }   _BlockBuilder = new Wings::BlockBuilder(_options_, _buffer_, _counter_, _finished_, false); } DriverBlockBuilder::~DriverBlockBuilder() {     if (_BlockBuilder != nullptr) {         delete _BlockBuilder; } }每个成员函数对应生成自己的驱动函数。类中的Add函数对应的驱动函数如下。Add驱动函数:int DriverBlockBuilder::DriverBlockBuilderAdd2(int times) {     Add2Times = times;     const char* jsonFilePath = "drivervalue/BlockBuilder/Add2.json";     Json::Value Root;     Json::Reader _reader;     std::ifstream _ifs(jsonFilePath);     _reader.parse(_ifs, Root);     Json::Value _Add2_Root = Root["Add2" + std::to_string(times)];     /*It is the 1 global variable: count    Add */     int _count = _Add2_Root["count"].asInt();     count = _count;     /*It is the 1 parameter: key Add2      * Parameters of the prototype:const Wings::Slice &key */     Json::Value _keykey_Root = _Add2_Root["key"];     /* data_ */     char* _keydata_;     {         std::string _keydata__str = _keykey_Root["data_"].asString();         _keydata_ = new char[_keydata__str.size()];         memcpy(_keydata_, _keydata__str.c_str(), _keydata__str.size());     }     /* size_ */     unsigned int _keysize_ = _keykey_Root["size_"].asUInt();     Wings::Slice _key(_keydata_, _keysize_, false);     /*It is the 2 parameter: value    Add2      * Parameters of the prototype:const std::string &value  */     string _value = _Add2_Root["value"].asString();       //The Function of Class    Call     _BlockBuilder->Add(_key, _value);     return 0; }构成以上驱动函数的主要包括全局变量、参数、调用被测函数的构建。以上是针对BlockBuilder类的驱动类的主要信息,而构建的程序遵守google的编码规范。一些命名规则如下:Wings生成的驱动代码,存储在drivercode文件夹中。(1)driver.cc与driver.h,针对程序中使用到的一些公共函数以及头文件。(2)同一个结构体或者联合体,可能会作为多个函数的参数使用,为了避免代码的重复,Wings针对所有的结构体和联合体的不同类型,封装成不同的驱动函数或者参数捕获函数。driver_structorunion.cc 存储结构体驱动函数的代driver_structorunion.h 对应的头文件。结构体实现函数的命名规则为:DriverStruct+结构体名字+类型,其中Point代表一级指针或者一维数组,PointPoint代表二级指针或者二维数组使用。源文件的命名规则为:driver_+源文件名/类名+.cc例如:driver_nginx.cc 或 driverBlockBuilder.cc驱动函数的命名规则:Driver_+函数名+(编号)例如:Driver_ngx_show_version_info(void);DriverBlockBuilderAdd2(int times)    (3)返回值的打印输出返回值的打印输出函数命名规则:Driver+Return+Print_+函数名。例如:DriverReturnPrint_ngx_show_version_info();(4)用户源代码中的main函数自动进行注释,重新生成一个main函数文件,来进行测试。Wings会生成驱动main的主函数文件为:gtest_auto_main.ccWings主要针对参数进行逐层展开,解析到最底层为基本类型进行处理。驱动的赋值部分,主要就是针对基本类型进行处理。(注:特殊类型,比如FILE等,后面会详细讲解如何赋值)以int类型举例,一般程序构成int的主要组成大概包括以下情况:int p; int *p; int **p; int ***p;int p[1]; int p[2][3]; int p[1][2][3];int(*p)[]; int(*p)[][3]; int *(*p)[]; int (**p)[];int *a[]; int **a[]; int *a[][3]; int (*a[])[];Wings会针对基本类型的以上15种类型,进行不同的赋值。构建完驱动程序之后,要对驱动程序进行运行,Wings构建googletest的框架,来进行测试。下面我们将针对期望断言的googletest程序进行构建。5.2 googletest程序的构建在构建完单元测试驱动程序之后,Wings将调用googletest的框架,完成对返回值的期望值验证代码,并且输出测试结果。Wings运行单元测试过程中,会构建返回值的保存代码,将程序的返回值结果进行存储,然后读取用户输入的预期值,进行结果对比,判断是否通过。针对具体的类,对应生成gtest类,每个gtest类中对每个函数构建期望代码。例如针对BlockBuilder类,生成的gtest类为 GtestBlockBuilder。GtestBlockBuilder类的声明:class GtestBlockBuilder : public testing::Test { protected:     virtual void SetUp()     {         const char* jsonFilePath = "../drivervalue/RecordDecl.json";         Json::Value Root;         Json::Reader _reader;         std::ifstream _ifs(jsonFilePath);         _reader.parse(_ifs, Root);         driverBlockBuilder = new DriverBlockBuilder(Root, 0);     }     virtual void TearDown()     {         delete driverBlockBuilder;     }       DriverBlockBuilder* driverBlockBuilder; }; BlockBuilder类中的每个函数对应一个gtest函数,每个函数负责调用DriverBlockBuilder编写的驱动函数,对于包含返回值信息的函数,则对应生成具体的期望值对比。 期望对比函数CurrentSizeEstimate:TEST_F(GtestBlockBuilder, DriverBlockBuilderCurrentSizeEstimate4) {     const char* jsonFilePath = "drivervalue/BlockBuilder/CurrentSizeEstimate4.json";     Json::Value Root;     Json::Reader _reader;     std::ifstream _ifs(jsonFilePath);     _reader.parse(_ifs, Root);     for (int i = 0; i < BLOCKBUILDER_CURRENTSIZEESTIMATE4_TIMES; i++) {         driverBlockBuilder->DriverBlockBuilderCurrentSizeEstimate4(i);         Json::Value _CurrentSizeEstimate4_Root = Root["CurrentSizeEstimate4" + std::to_string(i)];         /* return */         unsigned int _return_actual = _CurrentSizeEstimate4_Root["return"].asUInt();         /* return */         unsigned int _return_expected = _CurrentSizeEstimate4_Root["return"].asUInt();         /* return_expected */         EXPECT_EQ(_return_expected, _return_actual);     } }最后调用自动构建的main函数运行整个单元测试过程。5.3 参数捕获程序构建参数捕获是指在程序运行过程中获取程序的变量信息,主要包括函数的参数、全局变量、返回值等。Wings自动构建获取参数的程序,利用插装技术,将构建的捕获函数插入源代码中对应的位置,将获取的具体信息,写入值文件,可以将获取的数据作为单元测试的输入,在代码发生变更后,利用相同的输入判断是否得到相同的输出,进行回归测试。Wings的参数捕获代码存储在paramcaputrecode文件夹中。其中命名规则同驱动格式一样,将所有的driver替换为param即可。Wings针对每个类生成一个对应的参数捕获类,而参数捕获类中针对每个函数生成对应的捕获参数、全局变量以及返回值的函数。c++ 中类的成员变量是私有,无法从外部获取,Wings利用插桩技术,对每个类插入一个捕获函数,来获取类的私有成员变量。参数捕获类ParamCaptureBlockBuilder:class ParamCaptureBlockBuilder { public:   ParamCaptureBlockBuilder();   ~ParamCaptureBlockBuilder();   void ParamCapture_Reset1();   void GlobalCapture_Reset1();   void ReturnCapture_Reset1();   void ParamCapture_Add2(const Wings::Slice &key, const std::string &value);   void GlobalCapture_Add2();   void ReturnCapture_Add2();   void ParamCapture_Finish3();   void GlobalCapture_Finish3();   void ReturnCapture_Finish3(class Wings::Slice returnType);   void ParamCapture_CurrentSizeEstimate4();   void GlobalCapture_CurrentSizeEstimate4();   void ReturnCapture_CurrentSizeEstimate4(size_t returnType); };具体的捕获函数不再详细说明,具体信息可以在Wings官网下载试用版本查看。第六章 Wings类型以及面向对象语法特性的支持Wings能够支持任意的类型以及面向对象的语法特性。类型支持:l  基本类型,int、double、float、std::string等l  任意复杂的结构类型,结构体、联合体、枚举、链表、多级指针、数组、树、图等l  任意复杂的类对象,标准库容器、自定义模板类特殊模板:n  区分用户自定义的类型与系统变量类型(标准库头文件中包含的类型)n  类的运算符重载函数n  void *与函数指针n  识别类中包含delete与default关键字的函数进行特殊处理语法支持:u  处理static函数、保护和私有函数u  类中定义私有结构体u  类中包含delete与default关键字的函数进行特殊处理u  多态与复杂的类继承6.1链表针对链表类型,采用比较灵活的赋值方式,考虑到实际应用中的一些因素,针对链表类型,默认赋值两层结构,在实际测试过程中,用户可依据需要自动添加节点。6.2 标准库容器Wings能够支持c++的标准库容器,能够对容器进行逐层展开,利用不同容器的标准赋值函数进行赋值以取值。其他类似的容器例如QT中的容器以及boost库中的相关容器,我们在继续支持。6.3 自定义模板类一些用户自定义的模板类类型,Wings能够识别是用户自定义的模板类,Wings依据实际程序中的赋值类型,进行处理。6.4  void*与函数指针Void*与函数指针在实际程序中可以作为函数参数或者结构体与类的成员变量,针对不确定的赋值类型,Wings提供了具体的解决办法:① 利用编译底层技术,对源程序静态分析,获取真实类型,对真实类型进行赋值② 由用户在数据表格界面配置实际类型6.5 特殊模板在实际的代码程序中,会存在一些类型无法使用通用的模式全部展开,如一些系统变量(FILE、iostream)、第三方库以及一些用户需要特殊赋值。Wings是如何针对以上特殊类型进行赋值,举例如下:struct sockaddr_in {         short   sin_family;         u_short sin_port;         struct  in_addr sin_addr;         char    sin_zero[8]; };步骤如下:a.        识别sockaddr_in为系统变量类型,特殊标记b.        检测程序中的所有系统变量类型,显示在模板界面c.         用户配置特殊变量,如sin_familyd.        构建sockaddr_in对象e.        调用模板,生成驱动模板配置如下:图:6.5模板配置  第七章 数据表格Wings目前测试用例数据采用随机生成的方式,支持int、char、double、float、bool、char*类型。数据表格可以任意编辑以上类型的数值。(1)Wings数据表格将会针对参数进行展开,假如参数类型为结构类型,数据表格将分层展开结构的类型,到基本类型。(2) 针对基本类型的指针类型,例如int *p;Wings处理为不定长度的一维数组类型,int **p;处理为不定长度的二维数组类型,默认长度为3,数据表格界面可以点击进行添加和删除数据。(3) 针对不定长度的数组作为函数参数,例如int p[];Wings默认长度为1,用户依据需求,在数据表格界面进行任意添加和修改即可。图 7-1数据表格展示 附录A表一:type属性ZOA_CHAR_S/ZOA_UCHAR/ZOA_INT/ZOA_UINT/ZOA_LONG/ZOA_ULONG/ZOA_FLOAT/ZOA_UFLOAT/ZOA_SHOTR/ZOA_USHORT/ZOA_DOUBLE/ZOA_UDOUBLE基本类型StructureOrClassType结构体类型ZOA_FUNC函数指针类型ZOA_UNION联合体类型ZOA_ENUM枚举类型ClassType类类型  表二:basetype属性BuiltinType基本类型ArrayType数组类型PointerType指针类型StructureOrClassType结构体类型UnionType联合体类型EnumType枚举类型FunctionPointType函数指针类型 表三:其他属性Name代表结构体、类、联合体名字NodeType代表链表类型parmType代表函数参数类型parNum代表函数参数个数SystemVar代表此类型为系统头文件类型value代表枚举类型的值bitfield代表位域类型所占字节returnType代表返回值类型Field类成员变量Method类构造函数paramName类构造函数参数名paramType类构造函数参数类型TemplateArgumentTypeSTL结构参数类型WingsTemplateArgumentSTL结构嵌套参数名字TemplateArgumentValueSTL结构中参数为具体值FunctionModifiers函数访问权限FunctionAttribute函数是extern或者static函数FuncClassName函数所属类OperatorFundecl重载运算符函数Operator重载运算符类型
  • [分享交流] 精准测试白皮书2020版
    第一章 精准测试诞生的背景(附完整版精准测试讲解视频和白皮书pdf下载)20年前(2000年),上网是一件很酷的事,叫做“网上冲浪”,主要是几个门户网站占据绝大多数注意力;20年后(2020年),我们已经全方位“浸泡”在软件的海洋里,很多大型软件系统已经超过亿行,架构模型越来越复杂。未来的20年(2020-2040年),随着各种智能应用的层出不穷,软件系统内部逻辑会不可逆转的越来越复杂,而外部操作会越来越“傻瓜”。用户一个眼神或者一动脑,就能够切换出不同的功能需求。同时中国的软件产品化进程,近些年来正在蓬勃的进行,大量的公司开始研发自己的产品。伴随着关键核心软件的国产替代,使得原本应用级难度的开发正在向高复杂度和高质量的产品型研发转型。这个转型的过程,注定将产生对于高端测试工具的巨大需求。软件缺陷发现得越晚,其处理费用即呈几何性激增,而不是线性增长。如何从庞大复杂的系统中迅速及时地找到故障所在,一直是行业的一大难点。无法对大型软件进行有效的质量把控,就无法真正构建与维护大型软件。目前国内软件测试基本处于两种状态:一是绝大多数企业采用功能(黑盒)测试,二是小部分对软件产品有高可靠性要求的软件,企业会使用代码级的白盒测试工具。但这两种传统测试办法在目前的软件日趋智能化复杂化下,弊端越来越明显。功能(黑盒)测试技术**,使墨菲定律在所难免。**由于无法获知程序内部逻辑结构,这种测试办法对软件可靠性要求不高的应用来讲问题不是很大,但是对于大型金融机构、智能工业、航天军工等关键系统,就意味着时刻携带隐形的巨大风险。为此,功能测试后期需要极高的人力投入才能完成复杂逻辑的用例分析和设计。然而对于黑盒测试来说,程序越大,杀虫剂效应越明显。而行业内当作银弹的自动化测试,当自动化程序本身规模扩大以后,它的维护本身就存在了很严重的问题。低效的黑盒测试状态,最终将影响和制约未来整体软件发展。代码级(白盒)测试工具一般重点应用在研发阶段的单元测试上,满足了客户的部分高可靠性需求,但由于其价格高昂、技术老化,仅适合于小规模迭代瀑布式开发的软件,无法完成复杂的系统级别的测试以及分布式基于云的测试,更不适应敏捷迭代的开发模式。另外白盒测试工具基本都是国外产品,通常这些产品无法完成深度的定制化功能实现以及快速的用户响应,代码安全更是一个较大的问题。所有先进的科技都是具备前瞻性眼光的。随着国内军民各项大型核心软件系统的上马,研发一种面向高复杂度大型软件、自主可控的高性能智能精准测试平台的必要性,逐渐凸显。在这个时代背景下,2012年初,星云测试团队开始了筚路蓝缕、心无旁骛的研发征程。精准测试是个交叉学科,里面涉及到编译器、测试分析、图形技术、高性能通信与存储,软件的研发等多项底层技术。经历无数个不眠之夜对技术难点突破的煎熬与最佳解决方案的反复推敲,星云精准测试产品在诸多方面率先实现了重大技术创新,成功突破了白盒测试使用难度大、价格高昂的桎梏,有效消弭了国外高端测试产品垄断的壁垒。星云精准测试产品更偏向于软件测试业界的“灰盒测试”,即用简单的黑盒操作办法,可以同时得到单元级和系统级的精准测试数据。“星云精准测试”不负众望,在众多性能上大幅超越国外进口高端白盒测试工具产品,并在数据追溯、覆盖率可视化、智能回归、智能缺陷定位、分布式数据穿透与追踪等特性上有突出贡献。星云精准测试,既继承了传统功能测试前期的高效率运行区间,又能在后期通过系统的数据,让开发、测试充分协同,完成全程高效的测试与数据分析。(1)将测试团队的价值放大,能够将开发与测试更加紧密的连接起来,互为支撑。(2)采用精准、可信的测试技术,测试管理的难度大幅度降低。(3)降低企业对人员的过度依赖,通过系统适应人员的变更。(4)为企业实现测试数据资产的有效积累和分析,打下坚实基础。“星云精准测试VIP大企业离线版云平台”在整体测试试功能上的优异特性,成功获得了一批重要大型企业的高度认可及产品采购。星云测试的首发版本为:穿线测试 ThreadingTest,2014年6月6日上线,侧重于系统级白盒测试技术,测试用例和代码逻辑的双向追溯技术,测试示波器技术,覆盖率可视化技术。2015年8月6日,“穿线测试”正式更名为“星云测试”。在继承穿线测试整体技术上,星云精准测试增强了回归测试用例的自动选取技术,缺陷最后执行时序分析、智能缺陷定位、敏捷环境下多版本白盒测试数据的聚合、聚类分析、结合代码结构与动态数据的测试漏洞检出、代码安全特性,全面的测试管理特性等几十种优秀功能。目前有“星云精准测试VIP大企业离线版云平台”、“星云精准测试PASS在线云平台www.teststars.cc”、“全自动测试用例驱动生成系统Wings”等多种工具产品。星云精准测试旗下产品平台有Horn、Paw、Shell、Wings等系列产品。适用语言和平台暂为:为响应广大用户的需求,目前正在进一步扩展适应的语言和平台覆盖面。图1-1 精准测试在大型系统的效率运行分析星云精准测试,既保证了传统功能测试前期的高效率运行区间,又能在后期通过系统的数据,让开发、测试充分协同,完成全程高效的自动化精准测试。第一章 精准测试技术体系对软件测试技术的升级解析精准测试技术体系经过近6年的商业项目实践日臻成熟,大量商业应用案例的正面积极反馈,表明它已经成为新的软件测试技术方向体系。星云精准测试灵巧详细的功能设计和高度可靠的产品内核得到了市场的广泛认可,对于国际上原有的白盒测试工具来讲,是一种质的飞跃。2012年,精准测试的商用工具正式进入设计和研发阶段。与引进国外技术不一样,精准测试的核心技术体系是星云测试团队在国内的原创设计和研发。产品经过3年的悉心研发、反复内测、不断优化后,2014年精准测试在CSTQB会议上,发布了第一个商用版本ThreadingTest,引起了很大反响。6年前,行业上还全面沉浸在黑盒测试的大氛围中,从未见过精准测试的超前的运行方法和提供的强大智能的功能,由于理念与设计过于超前,早期有一些业内质疑的声音。但由于产品独到的优秀特点和巨大的潜在价值,诸多伯乐开始试用,给了产品在实际场景中不断优化、强化的宝贵机会。经过不同领域、不同客户的各种高复杂度大型系统的百般锤炼、验证后,自2016年开始,精准测试开始赢得全行业的重视和响应。优秀的商用落地性是精准测试技术流行的一个重要因素。由于精准测试把准了黑盒测试无法解决的难题和固有瓶颈,踏准了测试技术发展大的进程,并从设计一开始就特意注重了商业场景下使用要求复杂性,因此它从诞生之日起就不仅仅是一套理论,而是有着非常强的实用性基因和可扩展架构,多场景应用成果卓然。精准测试最底层的核心技术:“一种基于用例与源码双向追溯的测试装置及方法”,类似在重建量子纠缠的场景和数据。它成功的将功能用例和对应的代码二者之间的追溯路线,实现了精准无误的可视化。这彻底解决了在黑盒的状态下,软件工程师们无法获知程序内部运行轨迹的难题。自此开始,计算机处理海量测试数据的强大分析能力开始展现。我们知道,所有的黑盒测试都是人工进行的,它依赖的是人的算力,而软件正确性验证的工作量,主要是组合路径膨胀的问题,人的算力是远远跟不上的。那么精准测试如何从测试的角度对业务进行深度的测试辅助分析呢?我们用量子纠缠的形象类比来说明这个问题。量子纠缠理论我们可以简单理解为两个处在纠缠态的粒子一旦分开,不论分开多远,如果对其中一个粒子作用,另一个粒子会立即发生变化,且是瞬时变化。“一个粒子”会与“另一个粒子”产生相互作用,两个表面互相不关联的粒子,互相作用互相影响,他们对外是一个整体,无法单独描述“单个粒子”的性质,只能描述这“两个粒子”的整体性质,这就叫做“量子纠缠”。类比到软件测试上,我们的被测试软件中常见的软件界面和功能输入输出以及渲染它的复杂代码,他们之间就和两个纠缠在一起的粒子一样,具备强关联性。一个发生了变化,另一个必定发生变化。精准测试的另一核心功能是:“回归测试用例的自动选取”,就是基于用例和代码的追溯(量子纠缠)关系在进行运算之后全自动得出的。用例和代码精准的追溯机制,使数据开始可以实施大量的智能测试算法。在软件测试理论界,大量论文算法的基础都是默认建立在用例和代码的关系上进行分析的,但因其追溯机制工业实现难度极大,所以一直停留在学术理论阶段。星云测试引领的精准测试方法体系,落地在精确便捷的商用产品和大型解决方案中,让大量先进的测试分析方法和理论,得以实实在在的验证。精准测试首次明确的提出了:如何采集和应用双向追溯数据,进行测试辅助分析系统的构建。不管是纯软件系统还是运行在设备中的软件系统,用例与代码精准的数据可追溯性,都是基础性的强需求。比如金融转账业务、用手机拍照、机器人控制器的动作控制指令等,每个操作都有与之对应的代码。星云精准测试产品的强大之处在于:所有的分析算法不需要和被测系统的业务功能做特定的适配,因此可以应用于任意软件系统的测试辅助分析。而传统的白盒在产品实现上以采集和分析总体覆盖率为主,没有能力将覆盖细化到用例层级,因此传统的白盒测试囿于覆盖率的概念中,无法实现更高层次的测试辅助分析需求。星云测试产品的技术优越性及应用的易用性,使它成为新一代软件测试技术流的中坚力量。星云精准测试在企业应用场景中,为了方便客户还设计了完全静默的“傻瓜”运行模式,客户基本无需增加额外的学习成本。比如测试工程师打开测试用例的excel表格,当他准备执行某个用例的时候,只需点击这一项测试用例。随后通过VBA技术,直接调用星云精准测试的后台接口,再附加一整套深入应用后台执行线程的用户标签技术,就可以将用例和代码关联和分离出来(这里说的分离是指类似于J2EE服务端后台应用)。在对外提供多用户并发访问的情况下,可分离出每个用户执行的用例所对应的相应代码。前面我们提到功能和代码这两个量子数据的重建,以前主要出现在研发/开发人员相对比较零散的执行单步调试功能的时候(即单元测试),无法存储。国外白盒工具因其设计基因的限制,无法支撑超大型、高复杂度的系统级测试需求。在星云精准测试体系中,只要测试工程师执行测试用例,几乎不需要额外付出,在建立测试用例与数据的对应关系的同时,瞬间即可将海量数据实时采集并存储起来,用于后续测试大数据的分析和运算上。它的内核设计,使之可以轻松应用在数亿行的超大型应用上,实时采集、存储测试数据,而对原有系统的响应性能不产生干扰。星云精准测试对软件测试体系是重大的技术革新,它从以下几个层面改变并提升了测试:对软件测试的深度进行了大幅度的拓宽,让计算机有能力直接对测试用例进行辅助分析,给出更深入的测试决策分析依据。传统测试主要通过人工业务测试发现缺陷,定位缺陷必须由开发人员进行,二者信息没有精确的数据交互。精准测试主要基于测试人员标识的用例状态,以及自动记录的用例对应的代码路径进行频谱分析。它可以自动计算缺陷产生的代码位置,在发现缺陷的同时,同步产生缺陷定位的结果数据。精准测试提供的测试用例的聚类分析,是基于测试用例的路径空间距离进行聚类,聚类的结果可以直接对用例执行正确性进行审核,对用例进行等价类划分、分析缺陷密集区以及对用例执行分布进行评估等。精准测试提供的回归用例选取结果,经由海量的计算精准推荐而来,总体算法思路与人工分析的思路是类似的,采用人工智能算法模式把大量的计算完全交由计算机去总结分析,精准、稳定、可靠、高速。这个功能相当于解放了测试和开发团队大量的人力资源,彻底解决因团队成员变更而引入的未知风险。精准测试开辟的测试方法新体系,支撑了黑盒测试快速提升测试效能比的刚需。精准测试对于企业来讲,相对于比需要人工编写、维护庞大自动化脚本的自动化测试,工作量是低很多的。精准测试和自动化测试是并行的技术方向,企业应用可以根据各自团队的特点来选择,并没有绝对的应用时序依赖关系。自动化测试近些年的应用遇到一个广泛的批评就是:让测试团队疲于应付用例的编写而不是测试用例设计等测试核心技术。精准测试属于测试分析系统,着眼点和立意相对较高,在测试理论、方法和商业应用落地验证与成果上,形成较好的整体闭环。它把测试需要关注的核心,引领到一个正确方向上来。精准测试在技术特性上解决了测试结果可信性的问题。在精准测试之前,我们所见到的所有测试数据都是易伪造、易篡改的。例如我们通常使用的测试管理系统,一个用例是否被有效执行,绝大部分是在测试管理系统中被人工录入的。精准测试是在用例动态执行过程中,由计算机内部算法真实记录对应的程序运行逻辑,然后形成后面对测试数据进行精确分析的数据源基础。所以其底层程序运行的数据是没有办法也绝对不允许进行伪造和篡改的。精准测试果断去除了阻碍测试行业发展的重要障碍,让测试的结果精准化、可量化、可衡量化、可信化。此举,将大幅减低测试行业本身的管理成本,有效推动测试行业的快速、健康发展。精准测试大幅提升了开发和测试部门的协作效能。前面提到全景调试器,测试工程师执行完用例,即时产生代码层级的全景调试数据;测试完成后一旦用例存在缺陷,那么基于这个全景调试图以及高度可视化的数据路径追溯,开发人员可以直接对缺陷进行定位,不需要在开发环境下重现缺陷,再单步调试以解决问题。实施精准测试之前,开发人员苦于很难有效参与对测试用例进行审核;实施精准测试后,用例的执行结果都映射到了代码层,开发人员通过代码的覆盖视图很容易就可以协助测试人员补充和确认用例,开发和测试的协作变得非常简单和直接有效。      不可否认,在精准测试之前,开发和测试存在沟通不畅的问题。通过精准测试在项目中的有效实施我们会发现,精准测试可以帮助开发提供非常有价值的数据: 例如代码和用例的反向追溯可以帮助开发人员修改代码做参考,精准测试自动计算回归范围减轻了开发人员必须协助测试进行分析的工作量,因此,精准测试对开发和测试来讲,是一举两得、相得益彰的。星云精准测试发明了多个全新的、适应敏捷迭代开发模型下的覆盖率计算方法。覆盖率是白盒测试最基本的技术,但是随着敏捷迭代的开发模式,即使最高端的白盒测试工具已经几乎没有用武之地,原因就在于由于迭代很快,在某个版本上通常只能采集少量的覆盖率后新版本就发布了。本应在一个版本上分析的覆盖数据,分布到了数个代码结构不一样的程序版本上,因此原有的统计方法和参考意义基本上都失效了。精准测试提供了累计覆盖率,能够将多个版本的覆盖率以最新版本的代码结构进行投影,在控制流上进行累加。这样测试团队无需关注每个版本的覆盖情况,即可关注一个完整测试周期内所有版本的累积覆盖率。另外,考虑到敏捷迭代每个版本的测试需求,即:针对特定模块和功能范围进行测试(并不是全量测试),精准测试提供了相关覆盖率的计算方法。它可以自动圈定某个用例和用例集有关的代码范围(这些也是基于用例和代码的关系分析基础上动态计算的),在运行少量用例或者某个功能模块的情况下,它可以自动确定相关代码,把不相关的代码从覆盖率的分母中过滤掉。这样相关覆盖率在仅仅运行部分少量用例的情况下,依然具有很强的统计和参照意义。第三章 精准测试的定义精准测试定义:由中国团队发起并原创完成测试理念设计和商用产品研发的全新测试技术。旨在测试用例执行过程中,全自动建立任意运行模式的软件系统的功能点与源代码之间高度的可视化追溯机制,获取功能点相关的代码覆盖率并进行精准回归等测试用例深度辅助分析算法的应用。精准测试是测试理论界首次同时使用测试用例及其相关代码两个关键因子,进行质量综合考量和分析的创新测试方法体系。3.1 基本定义星云精准测试在测试用例执行过程中,可以同步全自动建立任意运行模式的软件系统功能点与源代码之间的高度可视化追溯机制,即通过内部算法能够将缺陷对应的代码出错位置直接定位出来;能够获取功能点相关的代码覆盖率并进行精准回归等,使测试用例深度辅助分析算法得以强化应用。这是软件测试首次同时使用测试用例及其相关代码两个关键因子,进行质量综合考量和分析的创新测试理论方法体系。这种有效的数据追溯与“量子联动”的突破性技术特性,大大增强了测试的深度与广度,打破了测试部门的成长天花板,为测试过程本身的价值挖掘和测试数据资产的增值,提供了必要而充分的条件。3.2 关键技术精准测试体系贯穿整个软件测试生命周期。精准测试主要侧重于系统级测试,在单元测试、集成测试、系统测试、验收测试、回归测试中,均能赋予测试数据强大的追溯能力。关键核心技术有:测试用例和代码逻辑的双向追溯,测试示波器、覆盖率可视化、回归测试用例的自动选取、缺陷最后执行时序分析、智能缺陷定位、敏捷环境下多版本白盒测试数据的聚合、聚类分析、结合代码结构与动态数据的测试漏洞检出等。精准测试在测试用例执行过程中,为用户从底层自动建立任意运行模式的软件系统功能点与源代码之间的可视化追溯机制,使用户获取用例级的代码覆盖率等多种高级测试数据。这一突破性的创新智能算法,有力的打破了软件开发、测试、维护及管理人员等之间的数据孤岛状态,实现了软件测试过程和结果的高度精准可视化,在软件高可靠性和高可信度方面,提供了全面而有力的数据支撑。精准测试运行模式是灰盒模式,即:可以在黑盒功能测试的时候,同步完成数据采集和分析计算,让“点测”团队也可轻易切入到精准测试模式,无需改变现有测试形式与流程,不增加额外的技术学习与转换成本。3.3 应用范围精准测试适用于任何形态的软件系统。星云精准测试可应用于各种软件包括嵌入式系统、分布式系统、web应用系统、单机软件系统等各类软件的测试,并且完全不受限于被测试软件本身的业务需求。3.4 用户收益实现从发现缺陷到预防缺陷的转型。减少因为缺陷而对整个研发运维流程造成的返工成本。使发现缺陷前移,减少因项目后期的严重缺陷而导致产品交付延期、成本增高,影响交付质量。建立跨需求、开发、测试等部门的测试协同平台。实现业务数据可追溯化和路径可视化。优化技术与业务方案。根据不同的质量要求,优先级顺序、技术实现手段等,实现智能灵活的技术方案推荐、交付和应变机制。减少无数据支撑造成的内耗。企业“精准众测”平台,支撑千人团队同时在线。测试数据可以根据管理需要做分析,为企业内的成本管控提供选型参考。实现跨地区,跨部门的业务人员,开发人员和测试人员的测试协同,即使不在同一办公地点的人员,也可针对测试需求,测试任务,测试用例和测试缺陷等方面进行远程沟通和实时协同作用,最终完成整个测试过程的实施。支持敏捷开发与测试模式。关注新功能的增量测试,支持自动化回归测试,实现高效的IT交付。第四章 精准测试的基础架构介绍4.1 精准测试的技术架构精准测试从某些层面来讲,赋予了测试用例真正的生命力。测试用例是测试数据的一种,当测试数据实现路线可追溯可视化的时候,一些高级算法的强大能力就可以显示出来。我们从精准测试的整体架构图中可以做一具体分析。图4.1-1 精准测试的总体架构图第一层:利用先进的前置编译器,为客户做源码静态结构分析(在客户的实际环境中,根据客户的需求进行相关的技术配合);第二层:将处理好的系统程序放入测试环境运行,测试工程师通过人工或程序自动化的形式,开始执行用例(人工执行用例可以和测试管理平台或者Excel表格方式进行对接),精准测试的 “软件示波器”采集运行数据并进行高速智能运算,获取精确的测试数据;第三层:根据采集的代码与对应的测试用例,在星云精准测试平台中实现用例与源码的互相追溯;第四层:通过精准测试的分析平台,可以对测试数据进行缺陷定位、用例聚类分析、回归测试用例和最小测试用例集等功能的计算,用户还可以根据需求,批量生成相应的测试报告,或进行测试数据高级分析。图4.1-2 精准测试的用例魔方在总体业务架构图的左上角区域,我们可以看到“用例魔方”几个字。大家可能会比较好奇“用例魔方”的涵义,它是精准测试中非常核心的高级回归功能之一。所谓“魔”是精准测试核心算法所赋予的超能力,所谓“方”实际上是代表测试用例的集合,每个测试用例用一个小方块标识,所有测试用例的集合用一个大方块。当我们把精准测试的和用例分析相关的功能画成架构图形表示的时候,它自然而然地看起来就像魔方。精准测试体系中,测试用例对应的代码逻辑精确而完整的实现了全自动化的追溯和存储,因此赋予了测试用例深入分析的基础能力。在精准测试的用例魔方中,目前存在三个面(随着后续功能的增加,将增加分析的面):即回归测试用例选取、测试用例聚类分析、测试用例最小化,同时辅之以智能缺陷定位技术。当测试用例与代码的追溯关系建立的时候,测试魔方的核心功能区即同步构建出来。为数据的多角度分析,提供了丰富的资源素材库。4.2 软件示波器软件示波器是星云精准测试独有的功能,它如同软件的质量运行情况“追溯穿行器”一样,在测试工程师按下启动键的同时,即时开始建立用例与代码的自动关联。示波器把采集到的测试数据通过可视化的窗口界面进行实时展示,内容涵盖采集到的块、条件和函数信息。蓝色波形代表写入的数值,黄色波形表示读取的数值,用户可以清晰的看到被测系统的数据变化。它如同心跳监控仪一样,如果被测试程序发生了崩溃,软件示波器就像人的心脏停止跳动一样,一根横线拉直向用户报警。如果正常采集到数据,会有持续的波形展示出来,高效而精准地监控程序细微的运行状况。示波器精密捕获每个软件单元任何微小的运行波动和行为改变,支持多次运行数据的比对。它可以根据需要记录崩溃前的至少50个块,使“崩溃重现”变得轻松简单。软件示波器中的测试用例可以从现有的测试管理系统导入进来,先选中用例点击开始,驱动被测试系统运行后,软件示波器就会采集到程序内部运行逻辑对应的波形信息。用例执行结束后,点击停止。这个用例运行阶段的数据,通过开始和结束的点击,边界就记录下来了。图4.2-1 软件示波器面板的正下方可以展示函数的各种调用信息。包括类、函数,参数类型等。清晰的列示出参数列表、类运行情况、内存检测数据、数据库拦截等多角度的数据分析追溯情况信息。示波器观测的维度较多,用例与代码的追溯是精准测试的基础功能,后面的高级算法都在这个基础上展开。用例和代码的追溯就像一个全景的调试器,只要功能由测试人员运行,所有的内部代码执行逻辑瞬间就可以展示出来。通过测试数据的反向追溯分析,开发人员可进行一致性修改,避免修改引入新的缺陷,通过正向追溯结果,开发可对用例的执行进行全面掌握,可用于快速修复缺陷和详细实现确认。同时软件示波器也提供一个辅助的等价类划分的功能,它将一个用例从开始到结束所执行的路径信息终值,完整记录下来。如果两个用例终值不一样,就可以确定为不是等价类。对于很多从功能表面很难界定是否等价类的测试用例,软件示波器可以给出精确结果。因此软件示波器的价值与意义在于:(1)只要测试开始执行,即可以透明方式高速采集功能运行过程中对应程序的运行逻辑。(2)在系统高速运转下采集,可保证对原有应用无干扰,超过1500w/s的采集速率。(3)可采集程序的条件,执行路径,执行参数,内存使用等动态运行数据。为了方便客户在运行项目测试时,一边对被测系统做实时数据监测,一边同步观察到示波器里面数据写入和读取的波形,星云做出了实时数据监测的悬浮窗缩略图,减少了切换带来的工作量。它不影响原有被测系统的界面,以半透明悬浮的方式展示在被测试应用界面的前面。图4.2-2 软件示波器悬浮窗4.3精准测试的双向追溯精准测试的测试用例和代码的双向追溯功能,是精准测试核心技术之一。如同前文的“量子纠缠”,即运行测试用例的同时,精准测试可以通过程序自动的记录并追溯到这个测试用例相应执行的代码。如果测试人员关注某一些代码行,它可以追溯出哪些测试用例在运行过程中运行过这段代码。通过这个技术特性,测试工程师的每个测试用例都可以进行量化分析和统计,提供了开发人员和测试人员之间精准的数字化交流依据, 增加测试和开发的交流效率。双向追溯技术记录了每个测试用例对应的程序内部的执行细节,细致到每个条件、分支、语句块的执行情况。开发人员亦可通过双向追溯的结果去理解程序逻辑,进行软件维护以及进行可一致性的修改。4.3.1 双向追溯技术正向追溯将测试用例和代码执行信息自动关联,可追溯到函数级别及代码块级别;通过正向追溯可直接把BUG定位到故障和缺陷逻辑相应的代码,并提供最后运行的时序数据;通过正向追溯自动记录产生功能对应的详细设计实现,辅助软件解耦和架构分析。图4.3.1-1 双向追溯(正向)-测试用例追溯到代码图4.3.1-2 双向追溯(正向)-测试用例追溯到代码4.3.2 双向追溯技术反向追溯将代码执行、函数、代码块级别和测试用例执行信息自动关联;通过反向追溯可直接观察代码变动所影响的测试范围;协助开发,进行代码修改后影响功能的范围评估;协助测试人员对代码修改部分所影响的测试用例进行评估。图4.3.2-1 双向追溯(反向)-代码追溯到测试用例图4.3.2-2 双向追溯(反向)-代码追溯到测试用例4.3.3 数据追溯技术-追溯测试用例的全景调用精准测试通过正向追溯把测试用例运行的代码执行进行了全景绘制,在全景图中,测试人员可以有效的观察到函数之间的整体的调用与走向,观察出被测模块与上层之间的调用关系。图4.3.3 测试用例运行的代码整体调用第五章 精准测试的核心组件与功能精准测试的核心组件与功能:软件测试示波器、用例和代码的双向追溯、智能回归测试用例选取、覆盖率分析、缺陷定位、测试用例聚类分析、测试用例自动生成系统等,完整的构成了精准测试技术体系。精准测试系统本质是一套强大的计算机开发与测试系统,实现了数据的可视化联动,以及开发辅助分析。它的关键技术赋予了测试用例和代码强大的相互追溯能力,随后衍生实现了很多高级测试功能与算法。精准测试系统将用例深入到代码层分析后,可以大幅改进人工测试所产生的各种问题。接下来将从风险控制、工作协同、敏捷迭代方面详细解析精准测试的核心功能和实际收益。5.1 风险控制5.1.1 七种测试覆盖率星云精准测试提供7种测试覆盖率:分别为:SC0语句块覆盖率、True覆盖率、Both覆盖率、CDC覆盖率、Branch覆盖率、MC/DC覆盖率。用户首先选择分析覆盖率的维度,例如选择了“SCO语句块”维度,那么系统就会将被测试程序的所有语句块结构展示出来,并且用颜色表达覆盖情况,绿色代表覆盖,深蓝色代表未覆盖。同时告知覆盖率的分子和分母都是哪些,非常清晰的展示覆盖率可视化结果。精准测试在前期已经对程序的静态结构进行了深度的分析,因此用户在覆盖率可视化界面,根据选择的维度在代码层面上把需要展示的结构单元都结构化的展示出来。例如图5.1.1-1 七种测试覆盖率中的第二张图 图5.1.1-1 七种测试覆盖率MC/DC覆盖率可视化MC/DC覆盖率,即修正判定条件覆盖,该覆盖率数据 MC/DC是DO-178B/C Level A认证标准中规定的、欧美民用航空器强制要求遵守的覆盖率标准。MC/DC覆盖率可以基本保证被测试软件无缺陷,对于大型系统的可靠性要求很高的一些关键模块,建议采用这个覆盖率标准。MC/DC覆盖简单来说,就是追踪复合条件中每个子条件的真假翻转,在其子条件不变前提下,是否都独立的影响了整个条件的真假值。星云精准测试系统会自动的把各种组合都列好,通过颜色表达哪些子条件已经满足,以及对应满足情况时其他独立子条件的组织情况。图5.1.1-2 MC/DC覆盖率可视化条件组合可视化展示精准测试对于多条件组合的代码,采用了最新研发的条件组合可视化视图进行展示。视图中,用户可以观察到每个条件的真假运行情况,以及条件与条件的组合运行情况。对于测试人员来说,当全部条件组合之间的T与F都完全满足时,即可实现代码全路径覆盖。图5.1.1-3条件组合可视化展示5.1.2 新增代码覆盖率敏捷模式下,因迭代频繁其存量的代码量很大,通常更关注增量覆盖度量。精准测试可以在程序新版本发布后,自动计算新增(变更)代码的范围,给出新增代码的覆盖率。覆盖率的分母中的函数都是变更和新增的函数。与此同时,基于反向追溯的功能,我们还可以给出新增代码对应的测试用例名称。当某个新增函数没有达到很高覆盖率的时候,我们通过反向追溯的用例,可以判定因为哪些功能范围的用例设计不充分,导致了新增代码覆盖率不高。图5.1.2 新增代码覆盖率5.1.3 测试覆盖率范围筛选与再统计在做精准测试或统计覆盖率时,往往测试管理者、开发人员、测试人员为了保证测试覆盖率的正确性,会对某个方法、类进行查看或在统计中把代码中一些废弃的函数、某些特殊情况下无法测试到的代码进行移除(至少是做相应备注),从而让测试代码覆盖统计率达到更加准确。星云精准测试在设计中,通过多种搜索、方法、类、模块过滤等功能,把需要统计的范围进行缩小、不需要统计的去除。根据用户的选择,进行覆盖率再统计展示。图5.1.3 测试覆盖率范围筛选与再统计5.2 工作协同5.2.1 打通开发与测试的隔阂精准测试打通开发与测试的协同工作通道,使得开发与测试能够更好的沟通,提高工作效率。传统模式下,开发人员关注的是代码,测试人员关注的是业务角度的测试用例,彼此的直接关联相对较弱。开发和测试的沟通,基本就是采用自然语言、Excel表格、内部系统等,存在交流信息不够严谨的问题。例如测试工程师发现一个缺陷,提交到缺陷系统,开发需要花费大量时间再行理解、准备数据、复现、调试,直到最后的修正。因为业务上的功能执行和代码并没有明确的关系,通常测试工程师执行完功能测试用例后,让开发人员帮助评审也非常困难。若测试工程师提供的测试结果都是比较模糊的功能逻辑描述,重现缺陷需要花费大量的时间。开发人员修改代码后,对于变更描述,以及变更引起的关联问题描述通常也都很模糊,导致测试又出现新问题。企业采用精准测试技术后,通过执行用例可以直接追溯到对应执行的程序代码块,这样的数据化沟通,将使开发人员和测试人员之间的协同工作效率大大提高。图5.2.1  协同模式5.2.2 源码动静态数据的统一星云精准测试通过插装得到的项目静态结构信息,结合测试后采集到的测试数据,能够精准记录测试的过程,通过这些静态数据和动态数据视图,便于开发人员基于图形化结果进行快速分析。对于不懂开发的测试工程师,通过程序控制流程图的图形以及通过颜色表示的覆盖信息,可以直接看到程序内部漏测的逻辑是什么,也可以通过这些结果直接与开发沟通,进行辅助用例和逻辑的补充。因为内部逻辑的强追溯性并且能够图形化的打开,可以有力保证黑盒测试后期开发快速理解并解决瓶颈问题,保持全程测试的高效执行。图5.2.2-1 源码静态结构与动态测试数据统一图(函数调用图)图5.2.2-2 源码静态结构与动态测试数据统一图(控制流程图)精准测试在程序静态分析的基础上,可以对程序绘制可视化的图形,同时将动态执行的覆盖信息染色到这些视图上。对于不懂开发的测试人员也可以很清晰的看到程序的哪些结构节点没有被覆盖到。例如图形中蓝色的节点是未覆盖的节点,绿色的是覆盖的,因为控制流程图本身有分支,嵌套等各种关系,测试工程师可以很容易判断出覆盖的范围大致是哪些。而如果有开发人员介入,可以更清晰地分析测试工程师执行的用例所遗漏的程序逻辑。图 5.2.2-3源码静态结构与动态测试数据统一视图5.2.3 缺陷最后执行时序分析星云测试可自动捕获缺陷或崩溃发生时,程序最后执行的详细路径信息。缺陷发生后,开发人员能够直接看到缺陷出现时,代码执行的时序和路径信息,直接定位缺陷并排查问题,节省大量的沟通、复现和调试的时间成本。当功能执行发现缺陷后,在软件示波器上可以立即按下“停止”键,那么最后执行的代码序列就可以被抓取到,开发人员可快速定位缺陷最后执行的50个代码块、条件、判断的各种执行信息。在下面视图中,我们可以根据标号(从1到50),看到代码最后的运行时序,在图形里面的每一个绿色小方块为一个代码语句块或者一个条件、判定,在一个大方框下的绿色方块代表一个函数内部的代码块。经过布局算法后产生下述图形。图5.2.3 缺陷最后执行时序分析5.2.4 智能缺陷定位通常测试工程师只是负责发现缺陷,缺陷的具体定位只能交由开发人员来执行。精准测试打破了测试部门的天花板,即通过内部算法能够将缺陷对应的代码出错位置直接定位出来。这相当于增加了测试深度,同时体现了测试数据和测试过程本身的价值。对于测试工程师来说,只要发现用例相似而程序在输出上有对错区分,就可以使用精准测试的智能缺陷定位功能。精准测试平台通过测试人员在功能测试阶段标记的用例执行状态,以及软件示波器自动记录的程序运行频谱,自动分析缺陷的出现的代码块。因精准测试平台可以获取每个用例执行时详细的路径追溯信息,测试工程师只要告知系统用例的状态,例如是否通过是否正确,那么精准测试内部算法就会去根据正确和失败的路径差异进行计算,给出缺陷出现具体位置的可疑度排名。这个功能可以大大增强测试在整个开发流程的参与度和测试数据价值,也让开发人员减少了自己去模拟场景的时间,快速提高开发和测试的协同和配合的效率。对于同类测试用例,经过多组测试可给出非常有效的结果。列出的可疑代码,可直接通过测试过程给出,提升测试的价值及产出。图5.2.4-1 通过功能测试频谱法分析进行智能缺陷定位选择可疑度算法、得到可疑度高的代码块,关联源码后,可根据代码可视化查看具体位置。可疑度计算有一个公式,并不复杂,通常每个代码块有2个变量,四种状态值。分别是:是否执行、是否通过,这样每代码块都有一个可疑度值。星云精准测试提供3种常用计算公式,供大家参考。aep表示通过且覆盖到该块的测试用例的个数、anp表示通过且未覆盖到该块的测试用例的个数、aef表示未通过且覆盖到该块的测试用例的个数、anf表示未通过且覆盖到该块的测试用例的个数。结果表示该块的可疑度。图5.2.4-2 智能缺陷定位展示5.3 精准测试对敏捷迭代的支持5.3.1 敏捷迭代下多版本白盒测试数据的聚合在敏捷环境下由于版本迭代速度很快,每个不用的代码版本上通常只能采集到少量的覆盖率。一旦发布新版本,就意味着代码发生了变化,覆盖率数据就需要重新采集,但是每个版本采集的少量覆盖率,从分析层面上并没有多大的意义。针对以上问题,精准测试给出了“**累计覆盖率”**的计算方法。它将一系列迭代版本的覆盖率,在最新的程序版本上进行投影累加。用户可将一个阶段各个版本的覆盖率累加起来进行分析。算法的思路是以最新版本代码为基础,以某个函数为单位,一直往前累加。直到这个函数在之前的某个版本代码发生了变化,就停止累加。例如下图函数A的4个版本的覆盖都可以累加,是因为这个函数在4个版本中都没有发生变化。而红色的函数B只累加了3个版本,是因为从版本v2.0-2后这个函数发生了代码变更。之前的代码覆盖就不能累加了。星云精准测试-敏捷环境下多版本白盒测试数据的聚合如图所示。图5.3.1-1敏捷环境下多版本白盒测试数据的聚合这种累加是在确保函数代码没有发生变化的情况下,在控制流上对相应节点的覆盖率进行累加。例如下图中3个版本,覆盖率不同的分支和控制流,数据累加后可以看到所有分支都覆盖到了。图5.3.1-2敏捷环境下多版本白盒测试数据的合并分析5.3.2 聚类分析星云精准测试提供的聚类分析功能,根据测试用例的函数执行剖面的向量化信息,对测试用例进行精确的空间距离计算后执行聚类分析。聚类结果可以分析被错误执行的用例,例如不相关的功能点聚类到一起,则说明其测试执行可能存在错误。精准测试提供的聚类分析功能,也可以辅助找到缺陷分布的密集区域。大部分情况下,缺陷分布会呈现2/8的聚集特性。在时间紧张的情况下,我们可以通过聚类结果,每个类选取中心点以及周边几个用例。如果没有问题,就可以去测试其他聚类,如果发现一个类缺陷概率高,那么这个类就需要进行重点测试。通过聚类结果可以分析测试用例的分布密度等信息,辅助进行测试决策。图5.3.2-1 测试密度下图中测试用例分类都有一个名字,这个名字是聚类结果中每个类中心点的测试用例名字,它基本标定了这个类的功能点范围。聚类的大小代表了某个功能范围测试是否足够充分。例如一些应该重点测试的功能,聚类结果中的用例数应该很多;一些小众的功能,聚类中用例数应该比较少。一个类中用例数越多,这个类圆圈比例越大,反之则越小。在聚类的基础上,我们也可以对一个类中的等价类用例进行分析。是等价类的用例,会分成一组专门展示。 图5.3.2-2 聚类分析5.3.3 覆盖率漏洞检出在敏捷迭代过程中,通常没有充分的时间将所有函数的覆盖率都达到一个很高的层级(Level)。精准测试结合代码结构和动态数据综合分析,通过计算直接筛选出潜在的高危测试漏洞,可以在短期内确定高危漏测模块并针对性的解决,帮助用户快速找到严重缺陷。当测试时间不充分的时候,执行完黑盒测试以后,先看测试漏洞列表,里面显示了通过静态信息和动态信息计算,得到的最高风险的漏测点模块。我们可以通过复杂度进行计算,因为复杂度高的模块一般来讲,它是相对重要的模块并且逻辑复杂,如果动态覆盖比较低,我们会优先筛选出来进行排序。处于调用和被调用中间的模块,因为属于中间关键模块,我们也会计算它的扇入扇出,和动态覆盖率信息。如果它的比率很高,将被认为是高风险模块,被筛选出来进行排序。回归时,应优先测试风险指数高的高危模块,补充他们的覆盖率。图5.3.3漏洞检测列表5.3.4 最小测试用例集精准测试也可以对用例集进行优化。比如用户有大量用例的情况下,尤其是自动化用例集含有长期维护的冗余用例。精准测试平台可以对很多重复用例的逻辑进行筛选和过滤,优化出满足当前总体覆盖的最小用例集。图5.3.4星云测试最小测试用例集第六章 精准测试支持不同剖面的分析报告在时间有限,经费有限,资源有限的情况下,我们既要考虑测试是否充分,也要顾及时间、人员、设备配置等限制条件。精准测试可以支持企业不同剖面的软件质量分析需求。6.1 详细的测试总结报告内容测试资源分析:多少人,多长时间、整体测试有效率及执行率测试结果分析:描述需求的测试结果,系统实现了哪些功能点,哪些还没有实现缺陷情况分析:缺陷复现、缺陷处理、缺陷数量、属性、状态分布,缺陷预防、缺陷收敛度度量指标分析:测试覆盖率、测试执行率、测试执行通过率、测试缺陷解决率效率指标分析:进度偏离度、缺陷发现率、用例执行效率和质量等高风险识别与排序:注明当前项目中面临的最严重、最优先的问题整体评估:哪些功能已经实现,哪些功能还未实现,还遗留哪些问题,遗留缺陷分析优化建议:测试过程优化,从测试组的角度为测试工作提出建议图6.1 星云测试的差异报告分析6.2 高级回归测试报告对前期测试执行阶段发现的问题、缺陷集中的功能,业务比较重要且使用频繁的功能进行再次测试,确保系统上线后,已被修复的问题不会重新出现,重要的、高优先级的业务不会发生错误。图6.2智能回归测试用例选取6.3 测试用例库评估报告软件测试的主要工作,是把软件需求映射为软件测试。测试用例是软件测试全过程的核心,也是测试执行环节的基本依据。精准测试充分满足软件测试执行过程中的各种指标:测试用例执行进度测试用例通过率测试用例颗粒度分析识别无用的测试用例识别冗余的测试用例从侧面提供增添新测试用例的依据从侧面提供调整测试用例库结构的依据图6.3 测试用例最小集6.4 精准测试的VIP企业内网私有云可信化报表星云精准测试提供多个剖面的高可靠性测试质量进度追踪报表。当客户端录入测试用例并采集数据后,用户内网web端将产生实时、详实的测试数据分析报表。该报表与普通的测试管理系统不同:普通的测试管理系统人为录入数据的情况比较多,使得数据状态的真实性没办法确切保证。精准测试提供的报表,底层数据来自于执行测试用例时候代码数据的采集,通过专用底层接口上传,完全无法进行数据调整或者篡改和伪造。通过浏览器登录测试系统,选择需要跟踪的项目,就可以实时对整个测试的质量、进度、人员进行精准的分析和管理。企业内网云端管理系统展示的数据基于精准测试数据的分析,所有数据原生精确,支持移动测试+本地测试。测试团队、开发团队、甲方负责人等多种角色都可以登录系统,从各个层面对测试、软件质量进行分析。图6.4项目汇总展示6.4.1 精准测试的VIP企业内网私有云-测试效率的直观展示精准测试报告可直观分析每天的测试效率,通过代码模块和复杂度关系图,看到函数群落测试情况分布及趋势,可直观精准识别系统测试所处阶段。每日增长覆盖率报表:管理者可以清晰的看到整个团队的效率趋势变化,比如刚开始测试的时候覆盖率增长快,到了黑盒测试瓶颈点上升就很慢了。这时候精准测试技术就开始发力,可以清晰地看到它在弥补效率损失方面的优势。覆盖率和复杂度报表:可以很直观地看到测试的质量深度,例如在测试不充分的时候,复杂度高的模块通常覆盖率都比较低,统计点分布自一个左上角的区域(表示高复杂度+低覆盖率),而当测试深入进行,这些点就会向右侧移动。管理者可以非常直观的看到系统测试的充分程度和上线的质量把握。图6.4.1-1 覆盖率每日增长趋势图与黑盒测试瓶颈图6.4.1-2 测试效率换档点与测试深度趋势观察表6.4.2 精准测试的VIP企业内网私有云-测试用例排行图测试用例排行分析报表:可直观展示参与测试工程师所执行的用例数、通过率和缺陷率,真实记录并分析每个测试用例的实效性。星云精准测试-测试工程师实效精准分析系统,将参与的测试工程师所执行的用例从逻辑覆盖映射到代码覆盖,真实记录并分析每个测试参与者的工作实效。以逻辑覆盖为基准的而不是用例数量为考核标准。图6.4.2 测试用例排行图6.4.3 精准测试的VIP企业私有云-测试用例双向追溯与覆盖率可视化通过用户的内网精准测试web报表,可以追踪每个测试用例执行的覆盖率和执行的函数路径信息,那些没有真正执行的用例,将无法伪造其对应的覆盖率信息。图6.4.3-1测试用例双向追溯追踪每个用例执行的函数信息以及具体的代码覆盖信息,在web端展示代码覆盖率视图,更具体的分析用例的执行情况。图6.4.3-1覆盖率可视化6.5 精准测试在数字化转型中的作用 完成数字化转型。提高软件交付质效、实现快速迭代持续交付,有效呈现测试价值。培养业务和测试的“两栖专家”。从不同角度提供有价值的数据依据,使测试团队既能熟悉业务知识、业务场景,又具备较强的业务分析评估和整合创新的能力。数据化交付。实现企业内部云平台建设、明确各工程活动环节的交付物和交付标准,并将质量验证标准、验证手段和监控工具嵌入流水线,保证各环节的有效性,对质量趋势进行提示和预警,及早发现缺陷,实现质量可视、过程可追溯、可审计。建立测试数据资源池,整合测试资产。利用大数据、人工智能等技术建立质量智能分析模型等,为“产品质量智能分析平台”提供有效数据。减少因人员变动而产生的成本影响。一般外包人员的流失率普遍为20-40%左右,重新招聘和培养,将对项目进度及成本进度,造成很大影响。因篇幅限制,完整版可查看 星云测试官网(www.teststars.cc)