4.3商业工具分类 本节介绍了用于汽车行业的各种测试工具。 这些工具可分为四大类:基于模型的测试生成,测试建模,验证和资源分析工具。 每个类别说明如下。 4.3.1基于模型的测试生成工具 鉴于所需系统行为的模型,这些工具由模型生成测试,在目标系统上执行测试以检查系统是否表现的与要求的一样。 测试是通过由模型按一定的度量“覆盖”其结构产生的。 大多数的由基于模型的测试生成工具所提供的覆盖度量的往往是控制流定向的,例如测试可以由覆盖模型的所有分支生成。 4.3.2测试建模工具 不是由系统模型生成测试,这些工具是由一个可能针对测试系统特定区域、可用于不同情况的抽象测试模型生成测试的。 该工具支持不同的符号来说明这些测试模型。 此外,这些工具能够在目标机器上进行所生成的测试,并评估相应结果。 4.3.3验证工具 有了所需系统行为的模型,这些工具就可以进行形式验证,就是说他们证明或否决该模型关于那些使用形式化方法的特定属性的正确性。 一个属性通常表现一个不良情况。一个验证技术是证明或反证是否不良情况在模型中保持不变的一种手段。当这种情况不变时,它产生一个反例证明。如果没有反例产生,这意味着该系统内没有这种情况。 有两种主要方法:模型检验和定理证明,被认为是汽车领域的形式验证。 对这些方法的文献综述超出了本文的范围,感兴趣的读者可以指向别处。 4.3.4资源分析工具 工具的第三类分析非功能特性,例如时间,内存使用情况等。举例来说,当危险发生时,重要的是要了解最坏执行时间( WCET )的防锁制动系统。此类分析在安全苛求的系统中极为重要。 过去,这样的分析是用一个特设的方式进行的:要么手动分析大量的系统仿真,要么通过在一些测试场景中运行该系统,观察其性能。然而,这些方法已经变得不切和实际,因为系统的尺寸和目标执行平台的设计的复杂性增加了,特别是在处理器里。 今天,专门的工具正在成为系统的验证过程用以覆盖这些方面的一个组成部分。 由于时间和内存特性与目标平台的结构特色一致,分析主要是对循环X测试的先进水平进行的。表1.主要在用工具 1 对于目标平台,请查看:http://www.absint.com/ait/trial.htm 2 IBV(基于仪器的验证)[10]是一项指定属性为一个连接到模型的显示器的技术。然后,它用制导模拟来寻找侵犯知识产权。笔者将这项技术归类为验证技术的一个变体。 4.4主要在用工具 本节提供了汽车行业精选在用工具的一份比较。这些工具是根据上面解释的术语分类的。 表1提供了工具信息表,包括:他们的供应商,类别,输入格式支持和循环X测试级别。大部分的输入格式是标准建模符号,可以参考相关文献了解详情。 5 .汽车软件测试工具/服务供应商 5.1主要竞争者 表2按生产国顺序列出了在欧洲提供软件测试工具和/或相关服务的主要竞争者名单,还包括那些在欧洲市场占有不可忽视份额的工具。这份名单包括公司提供的服务,主要工具,及其主要专业领域。名单中还包括在汽车行业深受肯定的工具应用笔记。 5.2其他竞争者 有些公司擅长嵌入式软件测试,但很少接触汽车系统。尽管这份名单并不详尽,但它包含了Testing Technologies( DE ) , Elvior ( EE)和Conformiq ( FI )公司 。 有些公司擅长于汽车软件测试,但没有紧密结合V模型(参见图1 ),没有大量应用代码级技术。其中最值得注意的是LDRA (GB ) ,Prover( SE ) , Coverity(US) ,Wind River(US)公司 。6.潜在机会 鉴于上述讨论,本节将会讨论一些技术以及潜在的机会。他们能够成为未来商用车领域软件测试的独特卖点。 6.1实时和连续行为 商用车嵌入式系统对硬实时约束规范很敏感。特别是,微秒范围内工作的传动系和底盘。没有实时性,就可能有沟通问题,尤其是连续变化的信号间。此外,实时行为在保证可重复的测试案例里是一个重要要求。 这篇文章中讨论的大多数工具是事件驱动性质的,极少能够表现连续信号和连续时间问题。 因此,应重点研究这方面的问题,尤其对于即将到来的新一代混合动力商用车。 6.2黑盒组件分析 汽车行业的黑盒组件分析还没有被充分挖掘。 如前所述,许多组件是从供应商那作为黑盒解决方案收集的。很难理解在系统中测试组件及其集成的行为[ 6 ] 。在第4节提到的种类繁多的工具中有几个提供这样的功能。其中一个就是从传统代码生成Simulink模型来检查是否反向工程模型符合代码行为的Reactis Tester。 当代码不可用时,弗劳恩霍夫商学院的RALT是唯一已知工具,能够保证基于运行时的系统观察的形式分析。表2.主要竞争者 1 AbsInt tools已经被应用于Bosch,BMW, Daimler, Honda, Mitsubishi和 Volkswagen。 2 RALT是从运行时系统观察派生出形式模型的黑盒系统的一种逆向工程工具。RALT已被应用于车门控制系统[8]。 3 EXAM是奥迪和大众汽车集团合作开发的,并已在内部使用[ 15 ] 。 4 TPT提供反应测试,反应测试就是:当传感器信号超过某一临界值时,立即精准地反应给系统。 5 MaTeLo已被应用到奥迪,Johnson Controls,Magneti,雷诺和大众汽车的汽车系统中。 6 SCADE已被用于商用车领域,尤其是在Liebherr公司的控制系统中[ 14 ] 。 7 Safety TestBuilder已用于测试Johnson Controls公司的 [ 12 ]轮胎压力监测系统。 8 CertifyIt已被应用于雷诺公司的汽车系统。 9 ENEA已成为DYSCAS (动态自配置汽车系统)项目( 2006-2008年)的一员,目前已被加入AUTOSAR标准[ 5 ] 。TD- Frame用于LabVIEW测试管理框架,并与美国国家仪器――TestStand的测试生成及执行相挂钩。 10 Reactis Tester还可以为了应用基于模型的测试技术,从源代码反向设计模型。Reactis Tester/Validator已经应用到Robert Bosch[10]公司的汽车系统中。 6.3安全性和可靠性分析 安全性和可靠性是商用车关注的重点。 现行做法中并没有用来分析可靠性的被认可的工具。对于安全性分析,一方面是进行最坏执行时间和内存使用情况分析,目前正使用专门的工具,如aiT WCET Analyzers和StackAnalyzer来执行。进一步增加安全性和可靠性方法以覆盖更多方面的潜力是很大的。 6.4工具链和GUI 对于嵌入式软件测试的各种不同的准则,一个适当的工具集是至关重要的。从可用性的角度来看,易用性和图形化界面是非常重要的。这方面的合理投资,加上几个案例研究的例子将有助于吸引汽车行业的从业者。7.结论 本文介绍了商用车领域的软件测试的当前做法的调查,也从软件测试的角度预测了这个领域的主要特性和潜在的未来机会。 调查发现,用于商用车的主要测试技术已被用于一般的汽车行业了。主要的原因是,商用车分享了其大部分的特征给其他类别的道路车辆。 但是,商用车需要优秀的方法来处理特定的工程问题。 值得注意的是,这些问题在现在的市售工具集行业里还没有得到充分解决。例如:硬实时功能,没有特殊的工具存在的安全性和可靠性问题。 此外,软件测试界还没有具体关注混合动力商用车将在不远的将来占据主要市场份额的问题。 尽管从业者已经开始在汽车行业宣传测试,但这与商用车领域的情况是不同的。 还有许多非正式的工具和方法,但关于质量和生产力的实际效用的经验数据却收集得很少。 因此,有相当大的机会,让利益相关者集中努力创立一个共同的平台,正式通过商用车领域里的最先进技术。
介绍 测试是汽车开发过程中的一个重要部分。随着软件越来越多地被应用于现代汽车,对严格软件测试方法的需求也变得越来越多。一个一直被忽略的特殊方面是:具有很多独特特色的商用车领域的软件测试的实践。 本文是对商用车领域软件测试的第一个全面的研究。但是,许多特征和相关结果可以外推到汽车行业的其他部分,而且更广泛地,还可以外推到嵌入式系统领域。我们通过对用于汽车行业的26工具和欧洲市场的20个工具/服务提供商的调查研究了现行做法。最后,还预测了未来潜在机会的一些方向。 本文希望能给汽车行业从业人员提供现被用于汽车领域的软件测试工具和服务的深刻见解。 由于本文重点是商用车领域,工具/服务提供商可以熟悉这一领域的潜在机会。 最后,对学生和研究人员来说,了解汽车嵌入式软件测试是如何在实践中进行的,及塑造汽车行业的新概念有哪些或许也挺有意思的。商用车领域 2.1定义 欧盟根据其结构及设备类型的设计目的来定义”商用车”,能够运载:a)超过九人,包括司机在内;b)货物和标准油箱[ 1 ] 的任何机动道路车辆都属于”商用车”。 轻型商用车( LCV )是车总重≤3.5吨的商用车的欧盟正式术语,符合该类别的车辆有面包车,小巴和轻型卡车等;重型商用车( HCV)是车总重>3.5吨的商用车的欧盟正式术语,符合该类别的车辆有货车,卡车,油罐车等。HCV的一个更广泛的定义里还包括农用车辆(拖拉机,收割机等),及施工车辆(岩石钻机,推土机,轮式装载机等)在内的重型设备和机械。 本文的研究范围涵盖了轻型和重型商用车。 2.2市场规模和潜力 商用车辆占有了汽车行业的一个具体且不可忽略的市场份额。按照ACEA (欧洲汽车制造商协会)数据显示,2012年全世界生产的超过20万台的商用车占据了欧盟市场11.3%的份额。较2011年,2012年欧盟产的LCV / HCV出口收入增加了22.9 %[3] 。 同样, Frost&Sullivan公司[ 2 ]指出,欧洲对轻型商用车的需求已经远远超过其他大洲。特别是混合商用车,在不久的将来将占据主要市场份额。一直会用到大约2016年的大多数混合LCVs将包括梅赛德斯-奔驰Sprinter和福特Transit Connect的电动版本。这两款车型有望占据欧洲混合轻型商用车市场的三分之一。 不看生产数据统计,伴随着全世界24%的年复合增长率[ 2 ],所有主要地区均有望保持商用车远程信息技术的增长速度。 2.3质量保证的挑战概述 在现代汽车的发展趋势已从纯机械转向广泛地电子化。 一辆典型现代汽车里的电子控制单元(ECU)粗略估计大约有70个,包括100多万的目标代码指令和近1 GB的软件[ 4 ] 。 这一趋势也反映在商用车的发展中。对嵌入式控制器越来越多的使用已或多或少地充当了商用车远程信息的催化剂。 这类车的价值创造主要是由嵌入式软件决定的,这不仅增加了成本和复杂性的,还增加了嵌入式软件的潜在缺陷。机械缺陷逐渐减少的同时,电子系统造成的缺陷却正在迅速增加[ 5 ] 。传动、线控、导航、人机工程学和信息娱乐类技术的进步要求嵌入式系统方法中有严格的质量保证措施。全球汽车业也普遍如此。 但是,商用车行业尤其受到旨在提高环境保护,安全( ISO 26262/IEC 61508 )和质量保证措施( IEEE 610 ) [ 9 ]的严格法规的影响, 。 为了满足当下目标,就要完全更新换代正在开发中的发动机,底盘和车身。所需解决的问题是:应该在商用车先进的嵌入式系统中使用什么样的,以及如何运用恰当的质量保证策略。 3.视觉测试领域特征 本节从测试的角度来描述:商用车领域的特征是相当重要的。 3.1安全性高要求 安全性是商用车的一个极其严格的要求。 欧洲道路评估计划的目的是:到2020年,要把欧洲交通事故的概率减少到零死亡。——该项目被称为Vision Zero。相关的标准,如ISO 26262 [ 9 ] ,也对汽车行业施加压力,使之为了让工程道路更安全去开发协议,工具和最佳实践准则。 还有一些其他专门针对商用车的安全措施。例如,重型卡车对由于开车时不经意地超出侧翻阈值而直接造成的翻车事件要多加注意。因此,制造商已经投入了相当多的时间和资源建立安全措施(例如:翻滚稳定控制系统)以应付重型商用车的这种情况。 3.2可靠性高需求 可靠性关注的是系统中故障率的量化。软件可靠性是一定执行时间内软件不会失败的概率。 迄今为止在汽车领域,相较于其他嵌入式系统领域如航空电子设备[4],可靠性并未受到正式管理。 此外,商用车应该在艰苦的,安全性很苛刻的环境下也能正常工作,如重型卡车装载数吨燃料,岩石钻机钻探不规则表面。因此,低可靠性会导致运行过程中出现危险情况。 他们的预期寿命要比正常客车长。所有这些情况都对车辆的可靠性和耐用性附加了要求。 3.3实时电子控制单元(ECU)功能 商用车嵌入式系统的复杂性很大程度上是因为大多数汽车系统的其它类并不正视实时性和界面限制。 对汽车软件工程实践的研究[ 4 ]表明,大部分车辆功能是由硬质和软质实时任务实现的。 极端情况下,多达95 %的功能由硬实时任务模拟,最有可能是商用车,因为对于商用车,像具有离散事件软实时功能的多媒体功能和人体舒适感功能并不太重要。 此外,要求不软不硬但有时又介于两者之间的功能,往往模拟为硬质[ 4 ]的 。典型的要求包括任务间的优先关系和抖动。 时间限制,例如截止时限在单个应用程序中可以变化多达三个数量级,通常从毫秒到几秒。 这方面的测试是极具挑战性的,因为一个系统的正确性不仅取决于其逻辑正确性,还取决于结果生成的确切时间。 往往很难追踪和再现错误,因为这需要对决定何时模拟系统及何时期望反应有很高的精确度。 3.4交错配置及变体
商用车的产品体积通常比客车小。而且,用户往往对诸如发动机扭转力,有效载荷等[5]技术规范要求更多。因此,汽车制造商经常用产品变体满足更大的客户群,从而增加市场份额及产品生产量。所以,商用车辆的嵌入式系统包括来自多个供应商的组件,且存在于大量的配置和变体里。
这就导致了为了覆盖庞大的产品变体而进行的测试活动方面的巨大的工作量。
3.5分布式开发
由于商用车部件变体的不一致性,汽车制造商不可能开发其所有的内部产品。相反,他们更愿意依赖第三方供应商成熟的专业知识。
由于这个原因,部分还因为严格的成本要求,商用车的发展在很大程度上被分散和外包[ 6 ]了 。这就形成了许多供应链关系使得规范最新及各级一致很困难。
事实上,制造商最终也没能在一个“黑盒”元件的内部设计出什么细节[4]。这就增加了定位单元和集成层次的组件测试误差的复杂性。
4 .汽车软件测试实践
本节概述了汽车软件测试里的实践情况。
本研究着眼于商用车领域,但本节的研究结果适用于整个汽车行业。
请注意,本节中的经验仅限于汽车行业主要名人们使用的主流做法。还有其他一些包括研究和技术创新的做法,被排除在本研究之外。
这里,我们的目的是提供一些明确的汽车行业的主要做法。
4.1基于模型的开发和测试
根据作者与主要汽车制造商、供应商合作的经验,现代汽车开发过程中模型驱动工程有着强劲的发展势头。这包括开发过程的各个阶段,即从需求到验证。
模型是表示系统行为一部分的一个抽象视图。
工程师很早期就使用特定领域建模技术为系统行为建模,使他们能够通过模型自动生成代码并在实施前测试系统,快速开发系统。
基于模型的测试的基本思想是:应用选定的算法由模型[13]自动生成测试,而不是手动创建测试用例。
除了自动化程度高,基于模型的测试还有助于由抽象层面来维持系统模型间的可追踪性并由系统执行层面来测试输出间的可追踪性。这就使得错误的来源容易追踪,也降低了整体测试活动的精力和成本。
4.2循环X测试水平
基于模型的开发和测试的不同阶段是用汽车SPICE开发标准所推荐的V模型(见图1)手动描述的。
V模型通常被设计来进行符合开发流程的不同水平的测试工作。
这些水平的测试被称为循环模型,循环软件,循环处理器(HIL)和循环硬件(又名循环X [ 7 ] )测试,说明如下。
MIL (循环模型) :
MIL提供可用系统(或子系统)模型的测试,且该模型及其环境没有任何物理硬件就可以进行仿真模拟。该系统的输入、输出界面被定义为关于不同的测试场景。
输入信号值进行了模拟,且相应的输出信号值与在该场景中定义的预期值进行了比较。
很早期甚至早在实施之前,MIL测试就可以进行系统的功能验证。
由于在同一台机器上的模型和环境类似,所以就不需要实时硬件了。
SiL(循环软件) :
SIL进行可执行代码段(或实施段)的测试。
在同一个机器上的操作情况相似。因此,此水平不需要实时硬件。
所有关于存储容量,数据结构等的细节都是由被模拟的环境控制的。这确保了任何“运行时错误”(缓冲区溢出,除以零,非法类型转换错误等)在实施时的检测。
此外,它还使系统行为可以通过运行MIL里同样的测试场景对模型进行验证。
PIL (循环处理器) :
PIL确保在目标处理器或目标处理器模拟器上运行时,可以测试实施过程。
执行环境通常仍是通过一个专用的实时仿真器模拟的。
这个阶段的故障可以与目标编译器(联动,优化选项,等等)或目标处理器架构联系起来。
MIL和SIL的测试场景在这可以用来与原结果进行比较,确保代码功能正确,即使是在它已被编译并在目标处理器上运行后。
HiL(循环硬件) :
HiL使现被嵌入实际硬件(ECU)中的实施得以被测试。这是通过把ECU和一个专用的实时仿真器连接起来完成的,此仿真器阐明了ECU对分析结果的测试环境的实际输入/输出。
ECU用来交流的嵌入式系统的其他部分仍然可以被模拟。
然而,它们与真实的电子信号进行交互。
HiL里的测试可以在实时环境中分析代码和硬件集成。
此水平的故障,也与输入/输出界面间的低水平通信,与嵌入式系统的其他部分的实时通信有关。
MIL和SIL的测试场景可以用来验证先前观察到的系统行为。

图1. V模型阐明循环X测试阶段
无论缺陷预防工作贯彻落实地多好,软件组件总有缺陷。这很明显,因为开发商无法阻止/消除软件开发周期的所有缺陷。因此,软件必须进行彻底的测试,然后才交付给最终用户。测试人员的责任是:设计既可以(ⅰ)找软件缺陷,又能(ii )评估该软件的性能,可用性和可靠性等方面的测试。 现在,为了实现这些目标,测试人员必须(往往是从一个非常大的执行域中)选择和/或制定测试用例的有限数量。不幸的是,完整的测试通常不是在这个范围,预算和时间的约束内实现和/或执行的。重要的是,当测试开始失控且不按计划地运行时,由于预期无法实际,测试人员往往承受了来自管理层和利益相关者的巨大压力。 因此,测试人员必须有效地计划测试并制定正确的测试用例,选择并执行合适的用例,监控过程,以确保有效利用工作资源和时间。所以,要列出这些无疑是一项艰巨的任务;要有效地实施,测试人员需要受过适当的教育和培训并拥有赢得管理层支持的能力。 一般情况下,测试人员会用两种不同的测试方法,其中,使用常规方法,测试人员主要是尝试用所有可能的输入去测试一个模块或组件,用所有可能的软件结构去实践。尽管这种做法仍在使用,测试人员却在慢慢灌输推理软件的一切的价值,最终使他们能够检测出所有可能存在的缺陷。但见多识广且有学问的测试员们都明白,这在现实或经济上是不可行,不可实现的目标。 现在,另一种方法可能就是让测试员们随机选择测试输入,希望这些测试能将大的缺陷找出来。不过,测试专家认为,随机生成的测试输入在评估系统的质量属性方面表现纪录欠佳。所以,从测试的角度来看,这是一个无休止的争论和悬而未决的问题。尽管如此,我们还是认为,测试员的最终目标是了解测试的功能、输入/输出域和使用环境,等等。 同样,对于某些特定类型的测试,测试人员也需要详细地了解代码是如何构造的。此外,测试人员也需要利用关于常在软件开发或维护过程中生成的特定缺陷的知识。有了这些信息,测试者就必须明智地选择测试输入的子集,以及被认为最有可能找测试过程中条件和限制内的缺陷的测试输入组合。然而,这个过程需要时间和精力。所以,测试人员知道且赞同:只有开发出基于执行的测试的有效测试用例,才能最大化和/或优化对时间和资源的利用。 “有效测试用例”我们是指:“一个很可能找出缺陷的测试用例” 。因此,制定有效测试用例的能力对于一个组织迈向一个更高质量的测试过程来说是非常重要的;反过来, 一个组织迈向一个更高质量的测试过程对制定有效测试用例的能力也有许多积极影响。例如,如果测试用例是有效的,那么: 检测缺陷的概率更大。 更有效地利用组织资源。 测试再用的可能性更高。 更符合测试、项目进度、预算,更重要地,提供更高质量的软件产品的可能性。测试用例设计方法 - 概念化 上面介绍了有效测试用例的种种好处,但缜密考虑测试人员用来设计这些有效测试用例的方法也同样重要。为了回答这个问题,有必要把软件作为一个精心设计的产品来查看和/或检查。现在,有了这个观念,就有两个基本方法可以用来设计测试用例: 黑盒(有时也称为功能或规格)测试方法。 白盒(有时也称为clear或透明盒 )测试方法。 使用黑盒测试方法,测试人员把SUT (测试中的软件)当作一个不知道其内部结构(即如何运作)的不透明盒子,测试人员只知道它的作用。使用这种方法的SUT的大小可以是一个简单的模块、成员函数、对象群、一个子系统、或一个完整的软件系统。此外, SUT的基础行为或功能的描述可以由正式规格,输入/处理/输出图( IPO),或一套定义明确的先决、后置条件来提供;重要的是,另一个值得一提的信息来源是:需求规格说明文档,通常描述SUT的功能,输入及预期输出。现在,鉴于上述来源,测试员提供指定输入到SUT,进行测试运行,然后确定所产生的输出是否与说明文档中提供的一致。因为黑盒测试方法只考虑了软件的行为和功能,它通常被称为功能测试,或基于规范的测试。这种方法特别有用,极有助于找到要求和规格中的缺陷。 与此相反,白盒测试方法关注将被测试的软件的内部结构。因此,使用白盒测试方法来设计测试用例,测试人员应该先了解结构,且为了实现这一目标,必须可随时参考和理解代码或适当的类伪代码的要求。一旦对结构有了必要的了解,测试者就可以选择合适的测试用例去实践特定的内部结构要素,并确定它们是否正常工作。例如,测试用例通常被设计来实践所有语句或发生在一个模块或成员函数中的真/假分支。但是,由于白盒测试的设计,执行和结果分析非常耗时,这种方法被限制和/或通常只适用于软件的小部分,如模块或成员函数。然而,白盒测试方法对于找出设计和基于代码的控件的逻辑缺陷和顺序缺陷,初始化缺陷和数据流缺陷等特别有用。 然而,从测试员的角度来看,要实现向用户提供低缺陷高质量的软件的目标,必须把这两种方法都用来设计测试用例。另外,这两种方法都支持测试员选择有限数量的将被用于测试的测试用例。这两种方法可以相互补充,因为每个都或许有助于找到某些特定类型的缺陷。重要的是,有了使用这两种方法设计出的一组测试用例,测试员找到SUT中各种不同类型缺陷的机会就增加了。 测试员还有一套有效的可再用的用来进行回归测试(更改后的重新测试),以及软件测试的新版本。 上面是一份概要:使用任一设计方法制定测试用例的各种可用方法。 但是,在使用任一设计方法准备测试用例前有一些因素需要考虑清楚。它们分别是: 测试相关风险。 预期缺陷类型。 测试员的知识和经验。 测试水平和必须进行分组和管理的小组活动。 用于执行测试用例的工具。 应用程序,软件和问题域的类型。 客户要求,等等。现在除了上述因素,以下几个要点和/或问题在选择正确的测试用例设计技术中发挥了至关重要的作用: 基于“经验”的测试用例设计 在基于经验的技术中,是人们的知识,技能和专业知识(关于域,技术等)构成了测试条件和测试用例的基础,且对制定测试条件和测试用例很重要。 在这儿,人们技术和业务两方面的经验都是绝对必需的,必要的,因为这给测试分析和设计过程提供了不同的角度。 重要的是,有了他们使用类似系统工作的丰富(前)的经验,他们或许对什么会出错,什么有助于测试有了想法和/或深入的理解。 因此,基于经验的技术与基于规范既与基于结构的技术偕行,又可用于没有规格,或者规格不足或过时的时候。 这可能是用于设计测试低风险系统的测试用例的唯一技术,但是这种方法可能在非常紧急的情况下特别有用,事实上,这是导致探索性测试的一个因素。 “随机”方式—考虑了吗? 通常,任何软件模块或系统都有输入域,从这个域里选择并使用测试输入数据建和/或执行测试用例。 现在,如果一个测试人员从必要输入域中随机选择输入,准备测试用例,并用它们来测试应用程序,这种方法被称为“随机测试”。 例如,如果一个模块的有效输入域是1到100之间所有的正整数,然后用这种方法测试人员会随机或胡乱地从该领域内选择值,如,选15 , 27和33。 但是,使用这种方法,也有一些一直无解的问题: 值(上面的例子中三个值)足以表明,执行测试用或运行例测试时,模块符合其规格吗? 是否有其他输入值,比那些(在本例中)被选中的值,更能找缺陷? 抑或有效输入域外的任何值应该作为执行测试用例的测试输入? 这就是说,测试数据应包括大于100的浮点值,负值或整数值? 因此,上述问题可以立即通过更加结构化的黑盒测试设计方法解决,尽管使用随机测试输入可以节省一些时间和精力,其他测试输入选择方法要求。 但是,根据许多测试专家,随机选择测试输入会产生一个有效的用于执行测试用例的测试数据集的机会非常小,并且对于一个更结构化的方法,随机方法生成测试输入的相对有效性总成为自省和/或研究的课题。 测试用例必不可少的部分—概念化 首先,设计一个测试用例用来回答这个问题:“我要测试什么? ” 。因此,对于测试人员来说,开发测试用例时周到地考虑很重要,这能够明确界定和/或提供需被验证以确保系统如期运行且能反映出它是用最高质量创建的信心的项目(模块,应用程序,子系统,或SUT )的完整概述。 现在,无论开发测试用例时用了什么设计技术/战略,测试人员都必须确保基本涵盖以下主要内容: 摘要 -应该反映实际的主题,类别和功能特性,使测试人员可以轻易地组织测试用例成逻辑组,并相应地对它们进行分类。 这部分可能具有关于基于测试时间,工作单元,和优先级等的执行工作的细节。它经常被称为测试用例的权重。 测试用例设计 - 这部分反映了测试用例的整体设计,其中可能包括一些高层次的描述。 正式审查 - 包含了关于必须审查或批准测试用例、并定义审批流程的团队清单的详情。 这部分主要是用来建立一个正式的审查程序,以确保业务流程符合标准。 此外,它可能包括关于测试用例所有人,工作项目,通知和成果总结等的细节 要求 - 本部分旨在:当要求被添加到测试计划中时,联系要求与一个特定的测试用例。 因此,一旦需求和测试用例间的联系被建立,测试人员就可以继续创建覆盖报告来了解和确定被测试用例覆盖的要求的比例有多大。重要的是,通过保持这种关联,有助于设置和检查整个项目的可追溯性。 先决条件 - 描述了形成前提的或必须在测试人员可以真正开始运行/执行测试用例之前发生的事物。 后置条件 - 不像先决条件,后置条件说明了需在测试用例运行/执行完成之后发生的事物。通常是产生适当的确认,如发送电子邮件通知等。 预期结果 - 本部分详细介绍了必须在测试员认为测试运行已取得成功前获得的结果列表。它可能包含了结果代码的文件或图像。 测试脚本 - 本部分概述了与特定的测试用例相关的测试脚本。通常,测试脚本有几种类型,包括手动测试脚本,关键字启用测试脚本,及其中每个测试脚本都包含用来实现一个测试用例的指示的自动化功能测试脚本。 在执行过程中,不像使用工具自动运行的自动化测试脚本,手工测试脚本是用语句处理语句。 测试执行记录 - 通常测试执行记录包含测试用例的详细信息,及从测试用例执行产生的高层次结果的细节。 重要的是,它们提供测试执行所需的相关硬件和软件环境的细节。例如,如果当运行在两个不同的操作系统和两个不同的硬件平台上,且使用了不同的浏览器的测试用例通过了,那么测试员可以为这些组合中的每一个创建测试执行记录。 测试执行记录还包含与该测试用例运行,测试运行的详细记录,以及所有执行结果的详细历史相关的的整体结果。 附件 – 本部分通常包含了支持测试用例的所有文档和文件。 风险评估表 - 本部分意在列出与某个特定的测试用例相关的风险。 所以,当上述所有部分都与测试用例相关,且如果这样的测试例被执行,那么就是一个好的迹象:关于实现完整的测试覆盖率,效率等方面的标准已达到。
之前做了一个项目,使用的是
oracle数据库,
数据库是建在本地
测试服务器上的;现需要将整个数据库数据结构及数据放到正式服务器上,现将整个
移动过程做一下记录,以做备用。
1、首先需要在正式数据库上创建和测试数据库相同名称的库名CSSP,创建之后可以到$ORACLE_HOME$\product\10.2.0\db_1\network\admin\tnsnames.ora这个文件下看到CSSP库的端口号。
2、打开浏览器进入http://localhost:1158/em 此处的端口可以到$ORACLE_HOME$\product\10.2.0\db_1\install\portlist.ini 下边查看。使用sys用户的超级管理员权限进入em管理,在“管理”模块下的“表空间”处创建测试服务器上相同的表空间名称,这里创建了 CSSPSPACE。
3、在客户端机器上使用oracle的客户端工具“Net Configuration Assistant”创建CSSP连接。
4、安装PL/SQL工具。
5、通过PL/SQL工具使用sys用户的sysdba权限连接CSSP数据库,找到user模块,创建用户duxiu,并给予connect和resource权限,退出PL/SQL程序。
6、使用PL/SQL连接测试服务器的数据库,在“Tools”-》“export user objects ”选项中,导出所有创建表,索引,主键,自增长序列,函数,存储过程,作业等
sql命令。
7、使用PL/SQL连接正式数据库,在“file”-》“open”-》“command file”中将上一步导出的脚本导入,并执行;这样数据库的结构都已创建成功了。接下来需要导一些数据进来。
8、使用PL/SQL连接测试服务器的数据库,在“Tools”-》“export tables”下,选中要导出数据的表,下边导出选项中选择“PL/SQL Developer”(“Oracle Export”导出选项试过不知道为什么导出之后,无法将导出的数据再导入进去,也没报任何错误提示;“SQL Inserts”只是生了插入的sql语句,导出效率等操作太差不推荐使用)。“compress file”,“include storage”,“include privileges”也都选中,在“Output file”中选中要导出的文件,点击“Export”进行导出。
9、使用PL/SQL连接正式服务器的数据库,在“Tools”-》“Import tables”选项中找到“PL/SQL Developer”选项,在这里只用勾选“Disable triggers”和“Disable foreign key constraints”,在“Import file”选项中找到刚才导出的数据文件,点击“Import”按钮将数据导入。
到此整个迁移过程已完成。
1.用system.IO 读写来实现,如果使用这个方式,每个测试生成一个报告,容易开启太多的线程,占用内存太多
FileStream ofs1 = new FileStream(path1, FileMode.Create);
StreamWriter owr = new StreamWriter(ofs);
2.开发帮助说使用vs com组件里边的引用,如下链接,感觉这个实现方式还更麻烦哪
http://blog.csdn.net/gisfarmer/article/details/3738959
3.再询问一个测试网友,他说可以用NPOI来实现,直接引用dll,写几句代码就能实现了,
下载地址:http://npoi.codeplex.com/releases
新建ecxel表格,在第一行第一列添加内容
excel行从1开始,NPOI内部从0开始;excel列从字母开始,NPOI是数字表示,记得转换
public void NPOITest() { HSSFWorkbook hssWorkbook = new HSSFWorkbook(); ISheet hssSheet = hssWorkbook.CreateSheet("new sheet"); hssSheet.CreateRow(0).CreateCell(0).SetCellValue("This a sample"); FileStream fs = new FileStream(@"d:\temp\test.xls", FileMode.Create); hssWorkbook.Write(fs); fs.Close(); } |
public void TestExcel() { FileStream file = new FileStream(@"d:\temp\test.xls", FileMode.Open, FileAccess.Read); HSSFWorkbook hssfWork = new HSSFWorkbook(file); ISheet iSheet = hssfWork.GetSheet("new sheet"); //获取所有行数,然后再+1的基础上加入数据 (lastRowNum是当前数据的最后一行) iSheet.CreateRow(iSheet.LastRowNum+1).CreateCell(0).SetCellValue("testtest"); FileStream fss = new FileStream(@"d:\temp\test.xls", FileMode.Create); hssfWork.Write(fss); file.Close(); } |
Bugfree是一款优秀的
bug管理和追踪工具,因此受到不少公司的青睐。但实际的
工作中,我发现不少开发或是
测试的同事有一些不好的使用习惯,使得我们对Bugfree的利用不够高效。我下面列出使用Bugfree的一些坏习惯,以此与各位测试同仁切磋使用这个工具的高效的方法。
对开发的同事而言,可能会有下面几条坏习惯。
坏习惯一:只采用默认的解决方案。
周围不少开发的同事,在解决掉一个bug的时候,往往只采用默认的解决方案:fixed。事实上,Bugfree提供了7种解决bug的方案供程序员选择。它们分别是:By Design、Duplicate、External、Fixed、Not Repro 、Postponed、Won't Fix 。这7种解决方案反应了程序员解决bug的理由。By Design的意思是,设计上就是这么定的,bug无效;Duplicate的意思是,这个bug已经有人提过,重复了;External的意思是,软件本身没有问题,是外部因素(比如
操作系统)造成的问题;Fixed的意思是:bug被解决掉了;Not Repro的意思是,这个bug无法重现;Postponed的意思是,这个bug推迟到以后解决;Won't Fix的意思是,是个问题,但是不值得解决。为什么会有这种习惯呢?我询问过一位开发的同事,他说看不懂英文,又懒得去查。另一位开发同事说,一开始没注意到这一项是可选的,时间久了,自然而然就视而不见了。
改掉这个习惯不是更好吗?我的理由:给bug设置正确的解决方案,一方面可以减少开发和测试的沟通障碍,让测试员知道程序员为什么要关掉这个bug;另一方面可以给bug归类,便于查找bug和开发后期集中解决bug。
坏习惯二:只在详细信息里写上:已解决。
由Bugfree提供的7种解决方案,不难看出详细信息这一栏多数情况下是为第四种解决方案Fixed提供补充的。很多bug在被fixed掉以后,如果只在这一栏注明已解决字样会有不好的影响。因为时间久了,或许程序员自己都不清楚这个bug是怎么被fixed掉的,如果再碰到类似的问题又要花很长时间去想办法解决,影响工作的效率。
改掉这个习惯不是更好吗?我的理由:在详细信息栏里注明bug被fixed掉的理由,一方面像上面所说的可以给开发人员提个醒,便于解决类似的bug;另一方面对测试员也有好处。测试员在碰到类似的bug以后,能够知道哪儿出了问题,这样就可以准确及时地提醒开发人员,便于bug的修改。
对测试的同事而言,可能会有下面的几条坏习惯。
坏习惯一:创建bug时,选错了项目。
周围有测试同事,在发现了bug后,就急急忙忙去bugfree里描述bug和指派bug,往往会忽略其他的选项。就拿这个项目来说,登录bugfree后里面就有默认的内容,但未必是和bug相对应的。如果测试人员因为发现了bug,有点兴奋,再加上一点粗心就会忽略这一项。
改掉这个习惯不是更好吗?我的理由:设置正确的项目可以给bug归类,便于bug的查找。若选错了项目,难免会抱怨找不到自己创建的bug,还得通过其他方式查找这个沉入“大海”的bug,影响了工作的
心情和效率。
坏习惯二:创建bug时,没有选/选错了模块。
创建bug时,模块这个选填项,不仅看起来不起眼,而且会让人误以为它没有用,所以测试人员往往会忽略它。 改掉这个习惯不是更好吗?我的理由:设置正确的模块,可以给bug分门别类。这样,开发人员就能很方便知道A模块有哪些bug,B模块有哪些bug,让开发人员对自己负责的项目模块心里有底。所以,还请测试人员辛苦下,把模块这一项设置好。
坏习惯三:设置错误的严重等级。
周围有同事,往往只注重对bug地描述,不去关注对bug等级的设置,不利于开发人员优先解决严重的bug。其实Bugfree提供可选的4种严重等级:1、2、3、4。1是最高等级,意思是这个bug导致系统死机,数据丢失或者与需求不符合;2是严重等级,意思是这个bug导致计算出现错误,功能实现出现错误;3是一般等级,意思是这个bug是个合法性问题,界面问题或是文档问题等;4是最低等级,意思是这个bug影响易用性。不少情况下是测试人员不清楚各种级别的含义,导致的分类错误。
改掉这个习惯不是更好吗?
我的理由:设置正确的严重等级,可以让开发人员优先解决1、2bug,在项目时间允许的情况下,再着手解决3、4类bug,以保证产品的质量。啰嗦一句,首先要保证产品能用,再去保证产品好用。
其实最好的习惯是按照bugfree的格式,把每一项该填的内容填好!!
最近,在Sixt(德国比较大的一个汽车租赁网站)上,我们把我们的开发环境从Eclipse迁移到AndroidStudio。这也就意味着我们进入了新的编译系统——Gradle,并且把TDD(
测试驱动开发)和CI(持续集成)纳入我们的
软件开发流程。这里不是讨论在软件开发中引入CI会带来怎样的好处,而是讨论在
Android中当测试UI之外的线程时会出现的问题。
Android中的测试(宽泛的定义)是一个
单元测试集合的扩展。涉及初始化、关闭测试,包含setUp()和tearDown()操作,使用反射的方式推断出不同的测试方式(从JUnit4开始我们就可以使用注释来指定的优先级和执行所有测试)。一个典型的测试结构如下:
publicclassMyManagerTestextendsActivityTestCase{ publicMyManagerTest(Stringname){ super(name); } protectedvoidsetUp()throwsException{ super.setUp(); } protectedvoidtearDown()throwsException{ super.tearDown(); } publicvoidtestDummyTest(){ fail("Failingtest"); } } |
这是一个非常明显的示例:实际开发中,我们想要测试例如HTTP响应、
SQL存储等等。在Sixt我们遵从一种Manager/Model方法:每个Model包含一个实体(车、顾客等)的表现。每个Manager用不同的模型(例如,我们的LoginManager可能需要用户与之交互的模型)聚合成一套功能。
大多数的Manager集中执行HTTP请求是要从后台获取数据。例如,我们用下面的代码来执行用户的登录:
mLoginManager.performLoginWithUsername("username","password",newOnLoginListener(){ @Override publicvoidonFailure(Throwablethrowable){ fail(); } Override publicvoidonSuccess(Usercustomer){ //.. } }); |
应用到我们自己的测试集合后,当得到预期之外的结果时,只是让这一结果失败。我们可以看到为什么在onFailure()函数中我们调用了fail()。接下来,即使我用一个错误的用户名也能通过这个测试。思前想后,测试似乎是按照代码顺序执行的,但并没有等到回调函数的结果返回再向下执行。
这显然不是一个好方法。因为现在的程序经常通过异步任务和回调方法从后台获取数据。尝试UIThread测试仍然不行。
最后,我发现下面这种方法可以行得通。只是用简单的CountDownLatch信号对象来实现wait-notify机制(你也可以用syncronized(lock){...lock.notify();},只是这样代码并不美观而已)
那么之前的代码就变成了下面的模样:
finalCountDownLatchsignal=newCountDownLatch(1); mLoginManager.performLoginWithUsername("username","password",newOnLoginListener(){ @Override publicvoidonFailure(Throwablethrowable){ fail(); signal.countDown(); } Override publicvoidonSuccess(Usercustomer){ signal.countDown(); } }); signal.await(); |
提到
Windows的UI自动化,不得不能不说Coded UI测试。Coded UI测试是
微软在VS2010里面推出的一个新功能,概念其实也不是很新,就是通过录制回放的功能来尽可能的简化Windows的UI自动化。
个人的理解,Coded UI的底层仍然是基于Windows TestAutomation SDK的Code,它的最大的作用就是把Code封装了一层,使之能为可以调用的方法,大大简化了
测试人员对于编码的硬需求,不懂C#或者VB的测试人员可以很容易的利用Coded UI开展自动化。
Coded UI不仅可以测试Windows的应用程序(据说对WPF支持的特别好),它也可以对Web浏览器开展测试,VS2010支持微软的IE和Firefox,我用的是VS2013,还没有来得及用这个东西测网站,我一般都用Robotframework + Selenium2library进行测试。
笔者最近在做一个和SCCM相关的项目,希望通过Coded UI能实施一些UI自动化的
工作,尝试了一下,感受如下
怎么用Coded UI
1. 先分析Windows应用程序是啥技术
这个估计测试人员用肉眼看不出来,可以请教开发,或者用一些工具帮忙看。Coded UI据说对WPF支持的比价好,对MFC支持的一般。
知道了这点,心里面可以有个数,对后面测试中可能的风险有个心理准备
2. 录制
打开VS,建立一个
Test Project,然后选Coded UI,VS2013的步骤大概就是这样,VS2010会复杂一点, anyway,然后差不多就可以开始录制了
录制的窗口很小,点击红色的按钮就可以开始录制了,录制没什么特别的,Coded UI会记录你的鼠标和键盘的操作,并把他们变成一些可以用术语表现得事件。
备注:录制的时候可以加入Assertion,这是为了判断测试结果的需要,否则录下来的就是一步一步的UI操作,特别注意。加入Assertion需要对Windows的控件属性有一点了解。
3. 调整录制结果
录制完了要点停止,之后点击中间的阶梯的按钮,就会出现录制的动作,这些动作都用很容易理解的步骤呈现在面板上。之后点击最右边的按钮就可以生成代码了。
需要给生成的代码取个名字,做为一个动作次序的标志
4. 调整UI操作次序以及其他属性
双击右侧Panel的UIMap.uitest,就可以打开UI操作的面板
重构是对软件内部结构的一种调整,目的是在不改变软件之可察性前提下,提高其可理解性,降低其修改成本。关于重构的至理明言如下:
任何一个傻瓜都能写出计算器可以理解的代码,唯有写出人类容易理解的代码,才是优秀的程序员;
事不过三,三则重构;
当你接获bug提报,请先撰写一个
单元测试来揭发这个bug;
当你感觉需要撰写注释,请先尝试重构,试着让所有的注释变得多余;
当你发现自己需要为程序增加一个特性,而代码结构使你无法方便的这样做,就先重构那个程序;
重构之前,必须建立一套可靠的测试机制;
写软件就像种树,优秀的程序员挖成小坑后随及填好,继续挖下一个,只会产生一系列小坑,不会有大坑,菜鸟则不会意识到所挖的坑正在变大,还是不停的挖,直到自己掉进大坑,爬不出来,陷入无尽的痛苦深渊;
开发时间越长,越能体会垃圾代码的痛苦,却不知道如何改进;
Kent Beck:我不是一个伟大的程序员,我只是个有着一些优秀习惯的好程序员而已;
变量(Variable)
不要定义一个临时变量多次重复使用,临时变量定义仍然应该可以自解释,从变量名称能够很好的理解变量的含义和作用。在定义一个临时变量后需要有一段业务逻辑才能够完成对临时变量的赋值的时候,可以考虑将这段逻辑抽取到一个独立的方法。
doublegetPrice(){ int basePrice = _quantity* _itemPrice; double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; } |
重构为:
double getPrice(){ return basePrice()* discountFactor(); } private int basePrice(){ return _quantity* _itemPrice; } private double discountFactor(){ if (basePrice()> 1000) return0.95; else return 0.98; } |
当遇到复杂的表达式的时候,需要引入解释变量,因为复杂的表达式很难进行自解释。
if ((platform.toUpperCase().indexOf("MAC")> -1)&& (browser.toUpperCase().indexOf("IE")> -1)&& wasInitialized()&& resize> 0 ) { // do something } |
重构为:
final booleanisMacOs = platform.toUpperCase().indexOf("MAC")>-1; final boolean isIEBrowser =browser.toUpperCase().indexOf("IE") > -1; final booleanwasResized = resize >0; if (isMacOs&& isIEBrowser&& wasInitialized()&& wasResized){ // do something } |
减少对全局变量的使用,第一个是全局变量的生命周期很难控制,资源本身无法得到很快的释放,其二是过多使用全局变量导致在调用方法的时候很难完全清楚方法说需要的入口数据信息,其三,多处都可以对全局变量赋值,我们很难立刻定位到当前全局变量的值来源自哪里?
分解方法(Extract Method)
一个较大的方法往往也会分为多个小的段落,step1,step2,step3,在每一个步骤都会考虑添加注释说明。而这些相对较为独立的步骤就可以分解为不同的方法,在分解后方法名可以自解释方法的功能而不再需要额外的注释。在一个类里面如果方法里面有一段代码在多个方法中重复出现,需要抽取该类的公用方法。在多个不同的类中有一段代码重复出现,需要考虑将公用代码放到公用类中形成公用方法。
方法名需要很好的自解释方法的功能,方法的返回尽量单一,方法的入口参数太多的时候应该考虑使用集合,结构或数据对象进行参数的传递。参数的传递可能出传递的是引用,但不要去修改入口参数的值。
不要因为一个方法里面只有一行,两行很短而不考虑去分解,分解的时候更多的是考虑代码的自解释性。代码本身不是解释的技术实现机制,而是解释的业务规则和需求。如果代码不是解释的业务规则和需求,那么其它人员就很难快速理解。
引入方法对象来取代方法,当发现一个方法只用到该类里面的几个关键属性,方法和类里面其它的方法交互很少,输出单一。由于该方法和这几个属性内聚性很强而和该类其它部分松耦合,因此可以考虑将方法和这部分属性移出形成一个单独的方法对象。
移动方法,类的职责要单一,一个类的方法更多用到了别的类的属性,这个方法可能更适合定义在那个类中。
class Account... private AccountType_type; private int_daysOverdrawn; double overdraftCharge(){ if (_type.isPremium()){ double result = 10; if (_daysOverdrawn > 7) result += (_daysOverdrawn -7)* 0.85; return result; } else return _daysOverdrawn * 1.75; } double bankCharge(){ double result = 4.5; if (_daysOverdrawn > 0) result +=overdraftCharge(); return result; } |
重构为:
classAccount... private AccountType _type; private int _daysOverdrawn; double overdraftCharge(){ return _type.overdraftCharge(_daysOverdrawn); } double bankCharge(){ double result = 4.5; if (_daysOverdrawn > 0) result += _type.overdraftCharge(_daysOverdrawn); return result; } classAccountType... double overdraftCharge(Account account){ if (isPremium()){ double result = 10; if (account.getDaysOverdrawn()> 7) result += (account.getDaysOverdrawn()- 7)* 0.85; return result; } else return account.getDaysOverdrawn()* 1.75; } |
分解类(Extract Class)
类的职责的划分不容易在初次设计时就准确把握,所以在编码时重构是必要的。职责定位不清!——典型特征是拥有太多的成员变量;而在这里面最重要的就是职责要单一,属性和方法是否合适的类中。如果不是就需要考虑分解或合并,扩展类的功能,或者抽象相应的接口。面向对象的设计原则如下:
1.单一职责原则(SRP)-就一个类而言,应该仅有一个引起它变化的原因
类的职责要单一,类里面的方法只做类的职责范围里面的事情。MVC即是一种粗粒度的职责话费,模型类重点是提供数据,控制类重点是处理业务逻辑,而V视图类则是关注数据获取后的呈现。
数据和数据操作可以考虑分解,如形成专门的DTO数据传输对象类。界面类和界面数据提供类也可以考虑分离,如形成专门的Facade层专门负责数据的准备和形成。界面层不应该有太多的数据处理操作。
当发现一个大的类里面的属性和方法存在明细的分组特性的时候,而且分组直接松散耦合,需要考虑分解为多个类。
引入方法对象来取代方法,当发现一个方法只用到该类里面的几个关键属性,方法和类里面其它的方法交互很少,输出单一。由于该方法和这几个属性内聚性很强而和该类其它部分松耦合,因此可以考虑将方法和这部分属性移出形成一个单独的方法对象。
胖接口也是违反职责单一,胖接口会导致所有实现接口的类都Override所有的接口方法,而有些接口方法往往是子类并不需要的。因此对于胖接口仍然要从职责的角度对接口进行拆分。
2.开放——封闭原则(OCP)-对扩展开放,对修改封闭
当发生变化时,只需要添加新的代码,而不必改动已经正常运行的代码:软件人的梦想!而要达到这个目的,关键是要能够较为准确的预测业务变化会导致的可能会发送变化的模块或代码。
3.Liskov替换原则(LSP)
子类型必须能够替换掉他们的基类型。正是子类型的可替换性,才使得使用基类类型的软件无须修改就可以扩展。案例参考正方形驳论。矩形的合理假设:长、宽可以独立变化;而正方形的合理假设:长、宽始终相等。因此正方形并不能从矩形继承。
4.依赖倒置原则(DIP)-高层模块不应该依赖于低层模块;抽象不应该依赖于细节。
依赖倒置原则的重点是高层模块类不要去依赖底层模块的类,而应该去依赖接口,特别是当我们预见到底层模块的类本身可能会扩展和变化的时候。这样在变化的时候最大的好处就是高层类和接口不用变化。
类是否考虑抽象为接口,一方面是根据LSP原则进行重构,一方面是需要观察我们建立的类,是否有多个类本身存在相同的行为或方法,如果存在则需要考虑抽象接口。
在当今竞争激烈的市场上一个APP的成功离不开一个可靠的用户界面(UI)。因此,对功能和用户体验有一些特殊关注和照顾的UI的全面测试是必不可少的。当涉及到安卓平台及其提出的独特问题的数量(安卓就UI提出显著挑战)时,挑战变得更加复杂。关键字“碎片化”象征着移动应用全面测试的最大障碍,还表明了发布到市场上的所有形态、大小、配置类型的安卓设备所引起的困难。本文将介绍安卓模拟器如何能通过使用一些技巧和简单的实践提供覆盖大量设备类型的广泛测试。
简介—分散装置里的测试
一般安卓开发者在其日常工作中面临的最大挑战之一是:终端设备和[url=]操作系统[/url]版本的范围太广。OpenSignal进行的一项研究表明,2013年7月市场上有超过11,828的不同安卓终端设备,所有设备在类型/大小/屏幕分辨率以及特定配置方面有所不同。考虑到前一年的调查仅记录有3,997款不同设备,这实在是一个越来越大的挑战障碍。

图1. 11,828 款安卓设备类型( OpenSignal研究, 2013年7月[ 1 ] )分布
从一个移动APP开发角度出发,定义终端设备有四个基本特征:
1.操作系统:由“API指标”( 1 ~18 )专业定义的安卓操作系统版本( 1.1~ 4.3 ),。
2.显示器:屏幕主要是由屏幕分辨率(以像素为单位),屏幕像素密度( 以DPI为单位),和/或屏幕尺寸(以英寸为单位)定义的。
3.CPU:该“应用程序二进制接口” (ABI )定义CPU的指令集。这里的主要区别是ARM和基于Intel的CPU。
4.内存:一个设备包括内存储器( RAM)和Dalvik 虚拟存储器( VM堆)的预定义的堆内存。
这是前两个特点,操作系统和显示器,都需要特别注意,因为他们是直接由最终用户明显感受,且应该不断严格地被测试覆盖。至于安卓的版本, 2013年7月市场上有八个同时运行导致不可避免的碎片的不同版本。七月,近90%这些设备中的34.1 %正在运行Gingerbread版本( 2.3.3-2.3.7 ),32.3 %正在运行Jelly Bean( 4.1.x版),23.3 %正在运行Ice Cream Sandwich( 4.0.3 - 4.0.4 )。

图2. 16款安卓版本分布(OpenSignal研究,2013年7月[1])
考虑设备显示器,一项TechCrunch从2013年4月进行的研究显示,绝大多数(79.9%)有效设备正在使用尺寸为3和4.5英寸的“正常”屏幕。这些设备的屏幕密度在“MDPI”(~160 DPI),“hdpi”(~240 DPI)和“xhdpi”(~320 DPI)之间变化。也有例外, 一种只占9.5%的设备屏幕密度低“hdpi”(~120 DPI)且屏幕小。

图3. 常见的屏幕尺寸和密度的分布(谷歌研究,2013年4月)[2]
如果这种多样性在质量保证过程中被忽略了,那么绝对可以预见:bugs会潜入应用程序,然后是bug报告的风暴,最后Google Play Store中出现负面用户评论。因此,目前的问题是:你怎么使用合理水平的测试工作切实解决这一挑战?定义测试用例及一个伴随测试过程是一个应付这一挑战的有效武器。
用例—“在哪测试”、“测试什么”、“怎么测试”、“何时测试”?
“在哪测试”
为了节省你测试工作上所花的昂贵时间,我们建议首先要减少之前所提到的32个安卓版本组合及代表市场上在用的领先设备屏的5-10个版本的显示屏。选择参考设备时,你应该确保覆盖了足够广范围的版本和屏幕类型。作为参考,您可以使用OpenSignal的调查或使用手机检测的信息图[3],来帮助选择使用最广的设备。
为了满足好奇心,可以从安卓文件[5]将屏幕的尺寸和分辨率映射到上面数据的密度(“ldpi”,“mdpi”等)及分辨率(“小的”,“标准的”,等等)上。

图5. 多样性及分布很高的安卓终端设备的六个例子(手机检测研究,2013年2月)[3]
有了2013手机检测研究的帮助,很容易就找到了代表性的一系列设备。有一件有趣的琐事:30%印度安卓用户的设备分辨率很低只有240×320像素,如上面列表中看到的,三星Galaxy Y S5360也在其中。另外,480×800分辨率像素现在最常用(上表中三星Galaxy S II中可见)。
“测试什么”
移动APP必须提供最佳用户体验,以及在不同尺寸和分辨率(关键字“响应式设计”)的各种智能手机和平板电脑上被正确显示(UI测试)。与此同时,apps必须是功能性的和兼容的(兼容性测试),有尽可能多的设备规格(内存,CPU,传感器等)。加上先前获得的“直接”碎片化问题(关于安卓的版本和屏幕的特性), “环境相关的”碎片化有着举足轻重的作用。这种作用涉及到多种不同的情况或环境,其中用户正在自己的环境中使用的终端设备。作为一个例子,如果网络连接不稳定,来电中断,屏幕锁定等情况出现,你应该慎重考虑压力测试[4]和探索性测试以确保完美无错。

图6. 测试安卓设备的各个方面
有必要提前准备覆盖app最常用功能的所有可能的测试场景。早期bug检测和源代码中的简单修改,只能通过不断的测试才能实现。
“怎么测试”
将这种广泛的多样性考虑在内的一种务实方法是, 安卓模拟器 - 提供了一个可调节的工具,该工具几乎可以模仿标准PC上安卓的终端用户设备。简而言之,安卓模拟器是QA流程中用各种设备配置(兼容性测试)进行连续回归测试(用户界面,单元和集成测试)的理想工具。探索性测试中,模拟器可以被配置到一个范围广泛的不同场景中。例如,模拟器可以用一种能模拟连接速度或质量中变化的方式来设定。然而,真实设备上的QA是不可缺少的。实践中,用作参考的虚拟设备依然可以在一些小的(但对于某些应用程序来说非常重要)方面有所不同,比如安卓操作系统中没有提供程序特定的调整或不支持耳机和蓝牙。真实硬件上的性能在评价过程中发挥了自身的显著作用,它还应该在考虑了触摸硬件支持和设备物理形式等方面的所有可能终端设备上进行测试(可用性测试)。
“何时测试”
既然我们已经定义了在哪里(参考设备)测试 ,测试什么(测试场景),以及如何( 安卓模拟器和真实设备)测试,简述一个过程并确定何时执行哪一个测试场景就至关重要了。因此,我们建议下面的两级流程:
1 .用虚拟设备进行的回归测试。
这包括虚拟参考设备上用来在早期识别出基本错误的连续自动化回归测试。这里的理念是快速地、成本高效地识别bugs。
2 .用真实设备进行的验收测试。
这涉及到:“策划推广”期间将之发布到Google Play Store前在真实设备上的密集测试(主要是手动测试),(例如,Google Play[ 5 ]中的 alpha和beta测试组) 。
在第一阶段,测试自动化极大地有助于以经济实惠的方式实现这一策略。在这一阶段,只有能轻易被自动化(即可以每日执行)的测试用例才能包含在内。
在一个app的持续开发过程中,这种自动化测试为开发人员和测试人员提供了一个安全网。日常测试运行确保了核心功能正常工作,app的整体稳定性和质量由测试数据透明地反映出来,认证回归可以轻易地与最近的变化关联。这种测试可以很轻易地被设计并使用SaaS解决方案(如云中的TestObject的UI移动app测试)从测试人员电脑上被记录下来。
当且仅当这个阶段已被成功执行了,这个过程才会在第二阶段继续劳动密集测试。这里的想法是:如果核心功能通过自动测试就只投入测试资源,使测试人员能够专注于先进场景。这个阶段可能包括测试用例,例如性能测试,可用性测试,或兼容性测试。这两种方法相结合产生了一个强大的移动apps质量保证策略[ 7 ] 。
结论 - 做对测试
用正确的方式使用,测试可以在对抗零散的安卓的斗争中成为一个有力的工具。一个有效的测试策略的关键之处在于定义手头app的定制测试用例,并定义一个简化测试的工作流程或过程。测试一个移动app是一个重大的挑战,但它可以用一个结构化的方法和正确的工具集合以及专业知识被有效解决掉。