性能调优的第一步是性能分析,下面从性能分析着手进行一些介绍,尤其对Linux性能分析工具vmstat的用法和实践进行详细介绍。
1、性能分析的目的
1)找出系统性能瓶颈(包括硬件瓶颈和软件瓶颈);
2)提供性能优化的方案(升级硬件?改进系统系统结构?);
3)达到合理的硬件和软件配置;
4)使系统资源使用达到最大的平衡。(一般情况下系统良好运行的时候恰恰各项资源达到了一个平衡体,任何一项资源的过渡使用都会造成平衡体系破坏,从而造成系统负载极高或者响应迟缓。比如CPU过渡使用会造成大量进程等待CPU资源,系统响应变慢,等待会造成进程数增加,进程增加又会造成内存使用增加,内存耗尽又会造成虚拟内存使用,使用虚拟内存又会造成磁盘IO增加和CPU开销增加)
2、影响性能的因素
1)CPU(cpu的速度与性能很大一部分决定了系统整体的性能,是否使用SMP)
2)内存(物理内存不够时会使用交换内存,使用swap会带来磁盘I0和cpu的开销)
3)硬盘(存储系统)
a、Raid技术使用(RAID0,RAID1,RAID5,RAID0+1)
b、小文件读写瓶颈是磁盘的寻址(tps),大文件读写的性能瓶颈是带宽
c、Linux可以利用空闲内存作文件系统访问的cache,因此系统内存越大存储系统的性能也越好
4)网络带宽。
3、性能分析的步骤
1)对资源的使用状况进行长期的监控和数据采集(nagios、cacti)
2)使用常见的性能分析工具(vmstat、top、free、iostat等)
3)经验积累
a、应用程序设计的缺陷和数据库查询的滥用最有可能导致性能问题
b、性能瓶颈可能是因为程序差/内存不足/磁盘瓶颈,但最终表现出的结果就是CPU耗尽,系统负载极高,响应迟缓,甚至暂时失去响应
c、物理内存不够时会使用交换内存,使用swap会带来磁盘I0和cpu的开销
d、可能造成cpu瓶颈的问题:频繁执Perl,php,java程序生成动态web;数据库查询大量的where子句、order by/group by排序……
e、可能造成内存瓶颈问题:高并发用户访问、系统进程多,java内存泄露……
f、可能造成磁盘IO瓶颈问题:生成cache文件,数据库频繁更新,或者查询大表……
4、vmstat详细介绍
vmstat是一个很全面的性能分析工具,可以观察到系统的进程状态、内存使用、虚拟内存使用、磁盘的IO、中断、上下文切换、CPU使用等。对于 Linux 的性能分析,100%理解 vmstat 输出内容的含义,并能灵活应用,那对系统性能分析的能力就算是基本掌握了。
下面是vmstat命令的输出结果:
[root@monitor-www ~]# vmstat 1 5 procs —————memory————— ——swap—— ——io—— ——system—— ——cpu—— r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 84780 909744 267428 1912076 0 0 20 94 0 0 2 1 95 1 0 1 2 84780 894968 267428 1912216 0 0 0 1396 2301 11337 8 3 89 0 0 1 0 84780 900680 267428 1912340 0 0 76 1428 1854 8082 7 2 90 0 0 1 0 84780 902544 267432 1912548 0 0 116 928 1655 7502 7 2 92 0 0 2 0 84780 900076 267432 1912948 0 0 180 904 1963 8703 10 3 87 0 0 |
对输出解释如下:
1)procs
a.r列表示运行和等待CPU时间片的进程数,这个值如果长期大于系统CPU个数,就说明CPU资源不足,可以考虑增加CPU;
b.b列表示在等待资源的进程数,比如正在等待I/O或者内存交换等。
2)memory
a、swpd列表示切换到内存交换区的内存数量(以KB为单位)。如果swpd的值不为0或者比较大,而且si、so的值长期为0,那么这种情况一般不用担心,不会影响系统性能;
b、free列表示当前空闲的物理内存数量(以KB为单位);
c、buff列表示buffers cache的内存数量,一般对块设备的读写才需要缓冲;
d、cache列表示page cached的内存数量,一般作文件系统的cached,频繁访问的文件都会被cached。如果cached值较大,就说明cached文件数较多。如果此时IO中的bi比较小,就说明文件系统效率比较好。
3)swap
a、si列表示由磁盘调入内存,也就是内存进入内存交换区的数量;
b、so列表示由内存调入磁盘,也就是内存交换区进入内存的数量
c、一般情况下,si、so的值都为0,如果si、so的值长期不为0,则表示系统内存不足,需要考虑是否增加系统内存。
4)IO
a、bi列表示从块设备读入的数据总量(即读磁盘,单位KB/秒)
b、bo列表示写入到块设备的数据总量(即写磁盘,单位KB/秒)
这里设置的bi+bo参考值为1000,如果超过1000,而且wa值比较大,则表示系统磁盘IO性能瓶颈。
5)system
a、in列表示在某一时间间隔中观察到的每秒设备中断数;
b、cs列表示每秒产生的上下文切换次数。
上面这两个值越大,会看到内核消耗的CPU时间就越多。
6)CPU
a、us列显示了用户进程消耗CPU的时间百分比。us的值比较高时,说明用户进程消耗的CPU时间多,如果长期大于50%,需要考虑优化程序啥的。
b、sy列显示了内核进程消耗CPU的时间百分比。sy的值比较高时,就说明内核消耗的CPU时间多;如果us+sy超过80%,就说明CPU的资源存在不足。
c、id列显示了CPU处在空闲状态的时间百分比;
d、wa列表示IO等待所占的CPU时间百分比。wa值越高,说明IO等待越严重。如果wa值超过20%,说明IO等待严重。
e、st列一般不关注,虚拟机占用的时间百分比。 (Linux 2.6.11)
随着计算机硬件水平的不断提高,计算机软件的规模和复杂度也随之增加。计算机软件开发从“个人英雄”时代向团队时代迈进,计算机软件项目的管理也从“作坊式”管理向“软件工厂式”管理迈进。这就要求软件开发人员特别是软件项目管理人员更深一步地理解和掌握现代软件工程的理论方法,完成思想观念上的转变。以下列举了10个在现代项目管理中思想观念上容易陷入的误区,希望能够抛砖引玉,引发大家更多的思索和讨论。
误区1:软件项目的需求可以持续不断的改变,而且这些改变可很容易地被实现。的确,在具体实际中由于种种原因客户方很难在需求分析阶段全面而准确地描述所有问题。随着开发进度的推进,往往会有一些需求的改变。而现代软件工程理论也利用软件的灵活性特点通过各种方式来适应这种情况。不过,这并不表明“软件项目的需求可以持续不断的改变 ,而且这些改变可很容易地被实现”。实践表明:随着开发进度的推进,实现软件需求更改所需要的代价呈指数形式增长。假定在需求分析阶 段实现需求更改需要花费1倍的代价;那么,在系统设计和编码阶段,需要花费1.5-6倍的代价;在系统测试阶段需要花费10-20倍的代价;在软 件版本发布以后,甚至可能要花费60-100倍的代价。由此可见,在项目开展过程中,软件需求的改变应当尽量早地提出。这样才可能花费少,容易被实现。
误区2:既然在项目人员配置中设置了专门的测试人员,那么软件所有的内部测试工作全部应该由测试人员完成。分析:软件程序测试可以分为“白盒法”和“黑盒法”两种方式。由于使用“白盒法”对测试人员各方面素质的种种要求,在进行程序测试时 测试人员总是最优先使用“黑盒法”。他们的工作方式往往是先对程序进行“黑盒法”测试;如果测试没有通过,不得已这才考虑对程序代码 进行“白盒法”测试。显然,这种对“白盒法”有意无意的“逃避”,对软件的可靠性和稳定性构成了威胁。如何解决这个问题?一方面需要 提高对测试人员的要求,另一方面也需要程序员完成部分的“白盒法”测试(实际上,程序员往往也是进行“白盒法”测试的最佳人选)。
误区3:软件项目管理只是相关技术部门的事情,与公司其他部门无关。在竞争日益激烈的今天,软件项目规模大、复杂度高而且时间要求紧迫。要想提高公司的软件项目管理水平,这就需要提高公司的整体参与意识,需要公司各个部门协同作战。例如需要会计部门协助进行项目预算,财务管理和费用控制;需要研究部门(技术委员会)指派专家 协助进行各种风险评估,提供技术指导;需要后勤部门提供各种保障。
误区4:在开发进度滞后的情况下,可以聘请更多的程序员加入到开发团队中,通过增加人力资源来赶上进度。分析:在注重团队开发的时代,开发方应该根据目前的软件项目管理水平慎重考虑这个做法。如果新加入的程序员对目前软件项目的应用行业 有一定了解,并且可以很快适应了开发方的项目管理方式、软件开发风格、团队协作氛围;那么“新人”的加入是有益的。否则,可能会“好心好意做坏事”。因为尽管其个人能力很高,但是为了使其与大家一起协同工作,开发团队不得不分出人手对其进行与项目有关的技术/业务培训,更重要的(也是难度最大的)是还要引导其融入团队。这可能需要花费开发团队许多时间和精力,很有可能使项目进度更慢。
误区5:技术骨干应该成为项目的项目经理,项目经理一定是所有项目成员中薪水最高的。分析:在“软件作坊”时代,这是一种普遍使用而且效果不错的方法;而在“软件工厂”时代,这种方法却带来各种问题,有时甚至直接导致 项目失败。究其原因这主要是因为随着现代软件开发分工的细化,对项目经理的要求也发生了根本的改变--最注重的不是其对某项专业技术 的掌握程度,而是其组织、领导、协调开发团队的能力(当然,可以两者均突出最好)。至于项目经理的薪水问题,这和定薪制度有很大关系。通常,项目经理执行的是管理人员的薪酬体系,而其他人员执行的是技术人员的薪酬体系。项目经理的薪水在项目成员中是比较高的,但不一定是最高的。有时候,为了激励技术人员,项目中的技术骨干得到的酬劳比项目经理要高。
误区6:只有项目经理以及部门主管才会关心项目整体进度,程序员只关心自己的开发进度。其实这是一种“官僚”的想法。实际上程序员作为团队中的一员,他不仅仅是在打一份工,更重要的是在参与一件“作品”的创作。在体味工作的辛苦的同时,程序员更重要的是要享受创作的快感。项目经理不应该漠视程序员对“成就感”的追求,应该向每一个人详细描述最终“作品”将会如何美妙和令人兴奋,并且在到达最终目标的路上设立一系列的里程碑。每当项目整体推进到一个里程碑的时候,项目经理应该把 这个消息告诉每一位项目成员,这不仅仅可以让所有的项目成员享受到阶段胜利的喜悦,还可以激发大家更大的工作热情,提高工作效率。
误区7:更大的压力可以带来工作效率的提高。软件公司的员工加班情况是时常发生的,对员工增加工作压力、要求加班赶进度,这种方式在初期可以略微提高生产力,因为员工喜欢压力,并且集中精力于项目任务,全力投入。中等压力或许可以将生产力提高25%,甚至使总的交付时间缩短25%。但是只有在压力处在适当的范围时,情况才是这样。压力再大点,增加的压力将不会产生作用,毕竟人的能力是有限的,当员工面对巨大的压力而习以为常时,会将普通的工作量占满整个工作时间,导致实际的生产力下降。如果压力再大一些,员工开始疲惫,直到筋疲力尽,甚至灰心丧气,他们对项目不抱有什么积极的态度,此时的项目结局可想而知。
误区8:使用高级语言可以大大提高项目进度,缩短交付期。高级语言相对于他们的前辈确实效率大大提高,程序员使用之可以提升编码速度,从而使整个项目的开发周期缩短;但是在完整的软件生命周期中,编码活动一般仅占总时间的20%左右,而需求搜集和分析、高层设计、测试等活动却无法从高级语言的使用中获益,所以不要认为运用了高级语言就可以制定一个“激进而且安全”的项目进度计划。
误区9:小型项目不需要严格的流程控制。小型项目由于涉及的人员较少,便很草率地制定一个开发日程表,没有认真地估计项目难度,结果实际完成时间与估计完成时间往往有较大差别;开发人员少,意味着不同人员的程序之间交互、接口相对少一些。开发周期短意味着往往是同样的几个人从头到尾负责一个项目。这两者都让人容易犯些错误。往往是几个人碰一下头,讨论一下最基本的数据结构、函数接口便分头去做自己的工作了,没有一份较正式的文档。往往觉得“把这些事情(流程管理、项目文档)都做完的话,项目就永远做不完了!”事实是如果项目中不做这些事,就得花更久时间才完成得了。
误区10:软件产品的质量完全取决于过程。事实上产品的质量受到人员、技术和过程三个要素制约,片面强调过程决定质量就好像认为只有明星程序员才能开发出合格的软件一样片面。而且低劣设计和良好设计之间的区别可能在于设计方法中的完善性,而良好设计和卓越设计之间的区别肯定不是如此。卓越设计来自卓越的设计人员。软件开发是一个创造性的过程。完备的方法学可以培养和释放创造性的思维,但它无法孕育或激发创造性的过程。
最近一直纠结性能分析与调优如何下手,先从硬件开始,还是先从代码或数据库。从操作系统(CPU调度,内存管理,进程调度,磁盘I/O)、网络、协议(HTTP, TCP/IP ),还是从应用程序代码,数据库调优,中间件配置等方面入手。
单一个中间件又分web中间件(apache 、IIS),应用中间件(tomcat 、weblogic 、webSphere )等,虽然都是中间件,每一样拎出来往深了学都不是一朝一夕之功。但调优对于每一项的要求又不仅仅是“知道”或“会使用”这么简单。起码要达到“如何更好的使用”。
常看到性能测试书中说,性能测试不单单是性能测试工程师一个人的事儿。需要DBA 、开发人员、运维人员的配合完成。但是在不少情况下性能测试是由性能测试人员独立完成的,退一步就算由其它人员的协助,了解系统架构的的各个模块对于自身的提高也有很大帮助,同进也更能得到别人的尊重。
再说性能调优之前,我们有必要再提一下进行测试的目的,或者我们进行性能测试的初衷是什么?
能力验证:验证某系统在一定条件具有什么样的能力。
能力规划:如何使系统达到我们要求的性能能力。
应用程序诊断:比如内存泄漏,通过功能测试很难发现,但通过性能测试却很容易发现。
性能调优:满足用户需求,进一步进行系统分析找出瓶颈,优化瓶颈,提高系统整体性能。
一般系统的瓶颈
性能测试调优需要先发现瓶颈,那么系统一般会存在哪些瓶颈:
硬件上的性能瓶颈:
一般指的是CPU、内存、磁盘I/O 方面的问题,分为服务器硬件瓶颈、网络瓶颈(对局域网可以不考虑)、服务器操作系统瓶颈(参数配置)、中间件瓶颈(参数配置、数据库、web服务器等)、应用瓶颈(SQL 语句、数据库设计、业务逻辑、算法等)。
应用软件上的性能瓶颈:
一般指的是应用服务器、web 服务器等应用软件,还包括数据库系统。
例如:中间件weblogic 平台上配置的JDBC连接池的参数设置不合理,造成的瓶颈。
应用程序上的性能瓶颈:
一般指的是开发人员新开发出来的应用程序。
例如,程序架构规划不合理,程序本身设计有问题(串行处理、请求的处理线程不够),造成系统在大量用户方位时性能低下而造成的瓶颈。
操作系统上的性能瓶颈:
一般指的是windows、UNIX、Linux等操作系统。
例如,在进行性能测试,出现物理内存不足时,虚拟内存设置也不合理,虚拟内存的交换效率就会大大降低,从而导致行为的响应时间大大增加,这时认为操作系统上出现性能瓶颈。
网络设备上的性能瓶颈:
一般指的是防火墙、动态负载均衡器、交换机等设备。
例如,在动态负载均衡器上设置了动态分发负载的机制,当发现某个应用服务器上的硬件资源已经到达极限时,动态负载均衡器将后续的交易请求发送到其他负载较轻的应用服务器上。在测试时发现,动态负载均衡器没有起到相应的作用,这时可以认为网络瓶颈。
性能测试出现的原因及其定位十分复杂,这里只是简单介绍常见的几种瓶颈类型和特征,而性能测试所需要做的就是根据各种情况因素综合考虑,然后协助开发人员\DBA\运维人员一起定位性能瓶颈。
一般性能调优步骤
一般性能问题调优的步骤:
步骤一:确定问题
应用程序代码:在通常情况下,很多程序的性能问题都是写出来的,因此对于发现瓶颈的模块,应该首先检查一下代码。
数据库配置:经常引起整个系统运行缓慢,一些诸如oracle 的大型数据库都是需要DBA进行正确的参数调整才能投产的。
操作系统配置:不合理就可能引起系统瓶颈。
硬件设置:硬盘速度、内存大小等都是容易引起瓶颈的原因,因此这些都是分析的重点。
网络:网络负载过重导致网络冲突和网络延迟。
步骤二:确定问题
当确定了问题之后,我们要明确这个问题影响的是响应时间吞吐量,还是其他问题?是多数用户还是少数用户遇到了问题?如果是少数用户,这几个用户与其它用户的操作有什么不用?系统资源监控的结果是否正常?CPU的使用是否到达极限?I/O 情况如何?问题是否集中在某一类模块中? 是客户端还是服务器出现问题? 系统硬件配置是否够用?实际负载是否超过了系统的负载能力? 是否未对系统进行优化?
通过这些分析及一些与系统相关的问题,可以对系统瓶颈有更深入的了解,进而分析出真正的原因。
步骤三:确定调整目标和解决方案
得高系统吞吐理,缩短响应时间,更好地支持并发。
步骤四:测试解决方案
对通过解决方案调优后的系统进行基准测试。(基准测试是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试)
步骤五:分析调优结果
系统调优是否达到或者超出了预定目标?系统是整体性能得到了改善,还是以系统某部分性能来解决其他问题。调优是否可以结束了。
最后,如果达到了预期目标,调优工作就基本可以结束了。
下面算是一个技巧,如面试官问到一个性能问题假设,我不知道性能问题出在哪儿时,可以按照这个思路回答^_^
● 查找瓶颈时按以下顺序,由易到难。
服务器硬件瓶颈---〉网络瓶颈(对局域网,可以不考虑)---〉服务器操作系统瓶颈(参数配置)---〉中间件瓶颈(参数配置,数据库,web服务器等)---〉应用瓶颈(SQL语句、数据库设计、业务逻辑、算法等)
注:以上过程并不是每个分析中都需要的,要根据测试目的和要求来确定分析的深度。对一些要求低的,我们分析到应用系统在将来大的负载压力(并发用户数、数据量)下,系统的硬件瓶颈在哪儿就够了。
● 分段排除法 很有效
性能测试调优应该注意的要点:
→ 要点1:在应用系统的设计开发过程中,应始终把性能放在考虑的范围内。
→ 要点2:确定清晰明确的性能目标是关键。
→ 要点3:必须保证调优后的程序运行正确。
→ 要点4:系统的性能更大程度上取决于良好的设计,调优技巧只是一个辅助手段。
→ 要点5:调优过程是迭代渐进的过程,每一次调优的结果都要反馈到后续的代码开发中去。
→ 要点6:性能调优不能以牺牲代码的可读性和可维护性为代码。
--------------------------------------------------------------------------------
本文只介绍了一些性能调优的要关注的东西以及性能调优的一般要点。并没有具体说如何对系统的每个部件进行调优,如何要细说也不是一两书能说清的,对知识面的要求也非常高,是我目前的能力无法触摸的。
这里做个总结:
《性能测试知多少》系列基本完结,虽然时间拉得比较长,但我没有把它给太监。虽然内容都在空谈性能测试理论知识,但我认为这些东西对于你从事性能测试工作必不可少。当然,我在“ jmeter基础 ” 与“ loadrunner 技巧 ” 中讲解两个性能测试工具的使用。
如果我的这些文章对于想了解和学习性能的同学带来一丝的帮助,我将非常开心。我不是高手,只是和你一起热爱测试技术的初学者,只是比较喜欢总结;也时常为前途迷茫,但我知道只要断去学习,路就在前方。我后面会整理性能调优的相关文章。
相关链接:
性能测试知多少----性能测试分类之我见
性能测试知多少---并发用户
性能测试知多少---吞吐量
性能测试知多少---响应时间
性能测试知多少---了解前端性能
性能测试知多少---性能测试工具原理与架构
性能测试知多少---性能测试流程
性能测试知多少---性能需求分析
性能测试知多少---性能测试计划
性能测试知多少---系统架构分析
性能测试知多少--系统计数器与硬件分析
性能测试知多少---测试环境搭建
性能测试知多少---测试工具介绍
摘要:本文针对软件质量的问题,通过可维护性、可靠性、可理解性和效率四个方面对软件进行质量评价,从而获得衡量软件质量好坏的标准。
关键词:软件质量;
1、引言
如何评价一个软件的质量,是最终获得高质量的软件的重要问题。以前,对小型程序,人们一般比较强调程序的正确性和效率,近年来随着软件规模的增大和复杂性的上升,对问题的看法已发生了变化。目前,软件质量的定义还是非常模糊的,人们对此尚未形成一致的看法,但一般说来倾向于从可维护性、可靠性、可理解性和效率等方面对软件作较全面的评价,下面分别讨论之。
2、可维护性
软件在运行阶段尚需不断“修正”,因为软件虽经测试但不可避免地总还隐含着各种错误,这些错误在运行阶段会逐步暴露出来,因而就要进行排错。
软件在运行阶段尚需不断“完善”,因为系统经过一个时期的使用后,用户必然会逐步提出一些更改或扩充要求,软件就需要相应地不断作修改。
软件在运行阶段往往还需作“适应性”修改,近年来计算机业发展迅速,一般在3至5年内,硬件或系统软件就会出现更新换代的新产品,于是应用软件系统也就需要作相应的调整或移植。
在运行期中,对软件所做的上述修正性、完善性和适应性修改,总称为“维护”,它涉及再分析、再设计、再编程、再测试等活动。考虑到大型软件系统的运行期可达10年以上,所以维护的工作量是极大的。
另外,维护工作也是相当困难的,由于软件逻辑上的复杂性,修改往往会带来新的错误。据统计,软件错误中有19%是由于修改造成的;有人还统计出,如果一个修改设计5至10个语句,修改成功的可能性是50%,如果一个修改设计40至50个语句,则修改成功的可能性下降至20%,因此软件维护是很困难,很冒风险的。
说明了在计算机软硬件的总投资中,软件维护所占的比例。可以看出,维护费用近年正在迅速上升,按这样的趋势发展下去,现有的人力物力将全部被束缚在维护原有系统上,就可能再也没有力量去开发新的系统了。因此软件维护引起了人们的普通关注,人们已意识到,一个软件系统及时其他方面都相当理想,但是如果不容易维护,他将不会有什么实际使用价值,所以,“可维护性”应该作为评价软件质量的重要准则。
3、可靠性
可靠性通常包括正确性和健壮性这两个相互补充的方面。
正确性是指软件系统本身没有错误,所以在预期的环境条件下能够正确地完成期望的功能。毋庸置疑,正确性对系统正常发挥作用是完全必要的。
对于一个小型程序,我们可以希望它是完全正确的,但对长达几万行甚至几十万行的大型软件,我们一般不能奢望它是“完全”正确的,而且这一点也是无法证实的。此外,一个大型系统运行时,完全可能遇到一些意外遭到意想不到的破坏。
曾有报道:“操作员手指的一滑使税收损失30万美元”。这就产生了一个新的概念——“健壮性”,其含义是指:当系统万一遇到意外时(具体是什么意外,事先是很难预料的)能按某种预定的方式作出适当的处理,如能立即意识到异常情况的出现,保护起重要的信息,隔离故障区防止事故蔓延,并能及时通知管理人员请求人工干预,事后从故障状态恢复到正常状态亦比较容易,所以健壮的系统应该能避免出现灾难性的后果。
正确性与健壮性是相互补充的,有些系统可能是正确的,但它不是健壮的;而健壮的程序并不一定是绝对正确的。
总的说来,可靠的软件系统在正常情况下能够正确地工作,而且在意外情况下,亦能适当地作出处理,因而不会造成严重的损失。所以,“可靠性”无疑是绝对重要的。人们宁可在开发时多花些代价,提高系统的可靠性,与发生事故后造成的损失相比,这些代价还是值得的。
4、可理解性
在相当长一段时间中,人们一直认为程序只是提供给计算机的,而不是给人阅读的,所以只要它逻辑正确,计算机能按其逻辑正确执行就足够了,至于它是否易于被人理解则是无关紧要的。但是随着软件规模的增大,人们逐步看到,在整个软件生命期中,为进行测试、排错或修改,开发人员经常需要阅读本人或他人编写的程序和各种文档。如果软件易于理解,无疑将提高开发和维护的工作效率,而且出现错误的可能性也会大大下降。所以,可理解性应该是评价软件质量的一个重要方面。
可理解性通常是指简单性和清晰性,对于同一用户要求,解决的方案可以有多个,其中最简单、最清晰的方案往往被认为是最好的方案。
5、效率
效率是指系统能否有效地使用计算机资源,如时间和空间等。这一点以前一直是非常强调的,这是过去硬件价格昂贵造成的结果。由于以下一些原因,目前人们对效率的看法已有了变化:首先,硬件价格近年来大幅度下降,所以效率已不像以前那样举足轻重了。第二,人们已认识到,程序员的工作效率比程序的效率远为重要,程序员工作效率的提高不仅能减少开支,而且出错率也会降低。第三,追求效率同时追求可维护性、可靠性等往往是相互抵触的。
所以,效率虽然是衡量软件质量的一个重要方面,但在硬件价格下降、人工费用上升的情况下,人们有时也宁可牺牲效率来换取其他方面的得益。
综上所述,一个软件系统的质量应该从可维护性、可靠性、可理解性、效率等多个方面全面地进行评价。对于不同的软件系统,各个目标的重要程度是不同的,每个目标要求达到什么程度又受经费、时间等因素的限制,所以在开发具体软件系统的过程中,开发人员应该充分考虑各种不同的方案,在各种矛盾的目标之间作权衡,并在一定的限制条件下(经费、时间、可用的软硬件资源等)使可维护性、可靠性、可理解性和效率等性质最大限度地得到满足。
一、什么是本地化测试
当今的软件发布到世界不同的国家和地区,使软件适应该国家或地区特定的语言、地区习俗和文化的过程成为本地化,这类的测试就称为本地化测试
二、本地化测试包括哪些内容
1) 翻译
这是最基本的内容,首先检查软件中显示的文字、帮助文档的内容等等是否翻译成当地的语言,并且能够被读懂。这并不是说要求测试小组中的每一个人都会当地的语言,只需要一人会就行了,不会的人可以检查本地化的其他方面。
2)文本扩展
当英文翻译为其他语言时,翻译的内容长度可能增加很多,这称为文本扩展。文本扩展会导致一些问题,例如窗口中的按钮文本截断,没有换行,或者按钮长度自动扩展导致窗口布局发生很大变化等等;有时还会导致系统崩溃,如对于文本分配的内存容纳英文文本是足够的,但是对于其他语言文本就不够了。
3)字符编码问题
有些软件使用代码页和DBCS(双字节字符集)提供对不同语言的支持,有些情况下会出现问题,例如没有代码页之间转换,显示的字符就会乱七八糟。
4)热键和快捷键
在本地化测试中需要测试所有的热键和快捷键能否正常工作,符合当地习惯。例如英文的Search翻译成法语是Rechercher,英文中热键是Alt+S,在法语中应该变为Alt+R
5)扩展字符
所谓扩展字符是指在普通英文字符A-Z和a-z之外的字符,例如许多键盘上看不见的象形字符。对这类的测试是,在所有接受输入和输出之处,尝试使用扩展字符,看能否像普通字符一样处理,如能否正常显示、打印,在程序之间复制粘贴会怎样
6)排序、大小写
不同的语言对排序规则不同,对于有些亚洲地区,排序是按字符笔画排序,欧美地区一般按字母顺序排序。另外,对于大小写问题,ASCII字符通过+/-32进行大小写转换,如果使用同样的方法处理其他语言,就很可能出错。
7)从左到右和从右到左读
有些语言,如阿拉伯文,是从右向左读,这个需要注意
8)图形中的文字
有些菜单中采用字符作为图标,例如使用B作为加粗,英文中是BOLD的首字母没有问题,如果在其他语言中,对于不懂英文的人,这个就可能是个问题,需要对图标进行改变。
9)字符串连接的问题
当前很多软件将要翻译的内容放到源代码之外的独立文件中,称为资源文件,当动态生成提示信息时,可能用一些文本碎片拼成一个大的提示信息,对英文来说没有问题,但是对于其他语言,由于文字顺序不一样,拼在一起就会错误。
10)内容
对一个国家是正确的内容,换到另一个国家就可能是完全错误的,需要仔细检查翻译的内容中适应符合当地的地区。例如对于有些国家是左侧行驶对的,但同样的内容在其他国家需要修改为右侧行驶才是对的。
11)数据格式
包括度量单位、日期、时间、电话号码、纸张大小等等,不同的国家使用不同的格式,除了检查格式正确以外,有时还需要相应修改代码,例如有的地区是以周一作为星期的开始,有的是以周日作为星期的开始
12)配置和兼容性问题
配置包括不同的外设,例如键盘布局、打印机等等,保证使用没有问题,如打印机能打印出软件发送的所有字符,在不同规格的纸张上打印出正确的格式。
兼容性包括在不同语言的应用程序中交换数据时,是否会进行度量单位的换算、扩展字符的转换等等。
在做
性能测试时,我们可能会遇到各种不同的业务需求与用户行为,在一个系统或网站中,每个用户的操作都不完全一样。我们如何来模拟这此用户的行为?经验与能力有限,我这里也做个简单的分析。
Action 介绍
在此之前,我们先来介绍一个Action ,Action就像是一个函数包,将用户操作根据类别存放在不同的函数中,当选择完HTTP协议后,VuGen将自动生成脚本的框架。
默认脚本目录有三部分组成:
Vuser_int
Action
Vuser_end
简单有的来说,我们可以把他们看成三个程序文件,他们依次按照Vuser_int --->Action--->Vuser_end 的顺序执行,存放于Action中的脚本可以循环执行(可以设置循环次数)
在脚本录制之前,我们可以设置将脚本录制在哪一部分:
在脚本录制的过程中,我们可以选择切换脚本的存放位置:
在脚本左侧右键添加新的action部分:
在菜单栏Vuser ---> run-time setting ,选择Run logic 选项,可以设置Action部分的循环次数。
下面简单介绍如何使用参数化、action设置和业务用户比例等进行性能测试。
场景一: 一个用户访问WebTours(loadrunner 自带程序)首页,做两次登录与退出
1、vuser_init部分录制访问webrours首页:
vuser_init() { web_url("WebTours", "URL=http://127.0.0.1:2080/WebTours", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t30.inf", "Mode=HTML", LAST); web_url("header.html", "URL=http://127.0.0.1:2080/WebTours/header.html", "Resource=0", "RecContentType=text/html", "Referer=http://127.0.0.1:2080/WebTours/", "Snapshot=t31.inf", "Mode=HTML", LAST); web_url("welcome.pl", "URL=http://127.0.0.1:2080/WebTours/welcome.pl?signOff=true", "Resource=0", "RecContentType=text/html", "Referer=http://127.0.0.1:2080/WebTours/", "Snapshot=t32.inf", "Mode=HTML", EXTRARES, "Url=../favicon.ico", "Referer=", ENDITEM, LAST); } |
2、将脚本录制部分切换到Action 部分,录制用户登录与退出
Action() { web_submit_data("login.pl", "Action=http://127.0.0.1:2080/WebTours/login.pl", "Method=POST", "Referer=http://127.0.0.1:2080/WebTours/nav.pl?in=home", "Mode=HTML", ITEMDATA, "Name=userSession", "Value=110416.933414338fzHQfHVpAVcfDtAHHptczAHf", ENDITEM, "Name=username", "Value={username}", ENDITEM, //参数化用户名 "Name=password", "Value={password}", ENDITEM, //参数化密码 "Name=JSFormSubmit", "Value=on", ENDITEM, LAST); web_submit_data("login.pl_2", "Action=http://127.0.0.1:2080/WebTours/login.pl", "Method=POST", "RecContentType=text/html", "Referer=http://127.0.0.1:2080/WebTours/nav.pl?in=home", "Snapshot=t33.inf", "Mode=HTML", ITEMDATA, "Name=userSession", "Value=110416.933414338fzHQfHVpAVcfDtAHHptczAHf", ENDITEM, "Name=username", "Value=test", ENDITEM, "Name=password", "Value=123456", ENDITEM, "Name=JSFormSubmit", "Value=on", ENDITEM, "Name=login.x", "Value=56", ENDITEM, "Name=login.y", "Value=4", ENDITEM, LAST); return 0; } |
run-time setting 的Run logic 选项,设置Action 运行两次。
运行脚本结束,可以通过菜单栏view--->Test Results 来查看运行的结果是否正确
场景二:
一个用户登录一个系统,做3次查询,5次插入,退出。
这里我就不做详细介绍了,需要的注意点是,可以在run-time setting 的Run logic 选项中点击insert Block 添加快,双击Block 设置循环次数。
将我们的查询操作与插入操纵分别存放在两个迭代块(block)中
我们还可以设置迭代之间的间隔,run-time setting 的pacing
三句话背景 科技子公司或者IT部门在一个大金融团体里面只能算是个成本中心,对IT团队来说,核心使命就是稳定运营、降低成本。这对于自动化测试来 说,意味着非常有限的资源预算、不稳定的测试环境、复杂的系统耦合关系、严苛的测试数据要求,还有那近乎无理的信息安全规范。如此种种,让我们并不能按照 自己想象的那样去实施自己的规划,以致会走很多弯路;而再回首你会觉得,有时候只是方法欠妥而不是资源不够,有时候只是导向错误而非技术不够高。
关键字:金融系统;自动化测试;单元测试;环境监控;测试数据;持续集成;
传统金融系统
以往,很多传统金融企业所使用的系统依赖采购和外包开发、维护的占比较大,这可能会让他们自己的IT队伍的经验少一些,而对商业工具、产品和解决方案的依赖性较强。而现在,这些IT队伍要么在新技术和互联网的压力下做艰难的转型,要么还在坚持着古老的体制文化,要么非常年轻,经验不足。他们的开发流程也有可能会臃肿僵化,也可能会做敏捷做得些敏而不捷,也可能测试自动化水平低下,也可能太过冒进——在我看来,这个行业里优秀的IT队伍远少于外面精彩的大世界,但是,无论是主动地还是被动地,他们都在随着时代在慢慢改变。
对于金融核心业务系统来说,大部分都是将前端按照业务属主的部门划分而做模块化设计。例如我们的保险系统,被拆分为网销、新契约、保全、理赔、渠道人 管、财务、电销与客服、查询与报表等等很多子系统;银行则可能会有客户管理、存贷款、外汇、国际结算、渠道、报表等等,有更多的模块。而无论前端如何拆 分,一个子公司的业务数据和大部分核心业务逻辑在后端都是共享的,所以整个系统群的基础业务数据和业务逻辑都是紧密耦合的。
在这个行业 里,我体会到,为了领先同业,仓促启动和上线的产品项目不胜枚举。因为这些项目的经营策略相关性或者政治敏感性较高,所以无论在DeadLine之前实现 了多少,只要没有致命问题,总要如期上线,而把遗留的问题通过后续排期解决。这种快餐式的设计和实现让很多项目和产品看似风光的按期上线,都附带了相当多 的系统债务的产生:紧耦合、难重构、难以自动化测试、运维成本高。而且这种债务是增量累积的,没有额外的人力很难清理掉。要么在日常工作之外付出额外的努力,要么任其自生自灭,造就“前人宿醉、后人埋单”的奇观。
在当今综合金融的大潮中,这些子系统必须满足的条件不仅是数据在系统群内共享,还要与集团其他子公司共享、与监管部门共享,以后也许会和医院与医疗机 构、社保机构甚至其他政府部门共享。看起来在我们现有的架构下,综合金融就意味着更多、更复杂的耦合,对测试来说就意味着测试数据使用难度的提升,从而对 测试的要求更高。同互联网一样,如今的金融产品也需要抢占市场先机,也需要快速的发布,但是快速发布不等于快餐式开发,否则久而久之系统维护债务的累积迟 早将达到无法负担的地步。而谢绝快餐式的发布,就需要新的开发模式,作为开发流程中的一个重要环节,软件测试也在被逼无奈地随着改变、转型,来面临新的要求。
时之沙聚金字塔
我们反复被教导,测试要越早越好:越早发现,修复成本就越低,所以就有了个分层测试的金字塔概念,而这个理论也经过很多优秀的公司的成功实践和论证,我 们不怀疑其正确性。但如果要做更多的单元测试,就需要代码有足够好的可测性,而现有的系统动辄就有着几十万、几百万行的陈旧代码,跨系统的逻辑调用比比皆 是,离传说中的高内聚、低耦合相去甚远。所以,在我们的实际操作中看起来,要做多少单元测试就要做多少代码重构,而做多少代码重构就需要多少能够守护代码 重构行为的测试。在这种情况下,只做代码重构或者只做单元测试的编写都是不靠谱的,这看起来是个死循环。
但是,不仅通过单元测试自动化 能够达成质量守护,通过GUI去做的自动化测试和手动测试也能够做到。虽然长期使用通过业务展示层去做测试自动化的成本很高,但是它的建设可以快速完成。 同时须知,同步做代码重构和单元测试编写的风险很大,如果在做的时候没有稳定的质量守护,作为涉费的金融核心业务系统,这种发布风险是不能被接受的。所以 比较现实的路看起来只有一条路:快速建设好GUI层驱动的自动化测试,用它来守护代码重构和单元测试。
我想借用这个沙漏图来说明我的观点:推动这种系统测试的 转型,不能幻想一蹴而就的单元测试的建设,可以考虑通过GUI来做测试自动化,将其做扎实,以其为基础来推动这些陈旧的系统群的自动化测试转型。而接下 来,要慢慢地用单元测试自动化来逐步替换这个基于用户展示层的测试自动化,直到它们之间形成一个合理的比例。在测试水平提高和转型的过程中,每个类型的测 试都有可能成为瓶颈;尤其是通过业务展示层来做的自动化测试,没有它,代码重构和单元测试自动化也无法稳步地推动。在现实中的人力配比下,聚沙成塔对我们 来说是一个神话,信念支持我们朝着目标持续迈进,但是在短时间内却不易企及。
单元测试纵与横 对绝大多数人来说,谈到单元测试,第一个冲入脑海的是“覆盖”这两个字,而其中部分人对覆盖率这三个字的关注度要远远高于四种覆盖设计方法。假 设我们的目标测试范围内有N个功能,平均每个功能有9个逻辑分支(含Exception分支)。如果要考量单元测试的覆盖方法,我想绝大多数人会选择每个 功能点选择少于4个的主要分支去覆盖。我的确也在公司的持续集成邮件组里看到这种争论,参与讨论的开发同事无一例外地赞同这种做法,他们主要考虑的因素是 ROI,他们认为在非常有限的时间里,将单个功能点的分支覆盖太多并没有太多意义,因为用户经常使用的是主要的那两三个分支。这种看法似乎符合常理,但实 际上并没有他们想象的那么经济,我实际上并不指望他们现在这个阶段做多么好的单元测试,但是却不愿意看到他们按照这种想法做下去。
我们不妨把每个功能覆盖主要分支,覆盖尽可能多的功能点的覆盖取向称做横向覆盖;对于每个功能点,争取覆盖尽可能多分支,而不计较有限的时间内 覆盖了多少功能点的覆盖取向叫做纵向覆盖。我个人的观点是:尽量采取纵向覆盖,自动化测试这同军事机械化作战一样,大纵深要比长战线好。我之所以在这种系 统群的测试转型过程中,对单元测试建设推崇纵向覆盖策略,理由如下:
1)时间短、人力少,短时间无法覆盖全面,无论纵横,双方在这一点上的论据一致。
2)一个功能的N个分支,如果在设计单元测试的时候只覆盖其中少数分支,在后续进行更深入覆盖的时候可能需要重构所有的测试以保证测试代码的逻 辑一致性和可维护性,这是一种重复工作的浪费。换言之,与被测代码一样,测试用例不能习惯于采取快餐式设计,而应该在一开始就针对该功能全面设计好。
3)对于这些待重构代码来说,全面考量一个功能点尽可能多的分支的覆盖,能够驱动更加完美的重构,反之,测试的设计与开发一旦分批进行,必然招致额外的重构工作。
4)至于有些开发认为在短时间内覆盖主要分支主要是因为用户平常只用那么几个主要的分支功能,我觉得恰恰相反。我们可以挑选用户使用最多的功能 而不是所有功能的主要分支,因为主要功能里面的“非主要分支”甚至是Exception分支同样有发生致命故障的可能。例如,对个退费转账,如果在某个异 常中没有处理好,可能会出转账数据反复生成的情况,这样的资金流入个人账户之后是基本再也无法追回的。这种故障即便快速恢复,也远比那些次要的功能不可用 招致的损失大。
5)有些人认为,那些不重要的分支,在UI回归测试的时候着重测试一下就好了,这更是违背金字塔模型核心的经济原则。要知道UI回归测试的强项 在于对主流程的覆盖,而分支和异常通过它去覆盖本身就是非常难的事情;既然认同单元测试的经济性,还要依赖GUI测试去做那些非常难以实现的分支的测试, 这岂不是自相矛盾么?
6)此外,这种纵深覆盖更能锻炼测试代码编写者的测试思维,完整的经验比被阻断的实践更具参考价值。后续编写新的功能模块的代码时,会因为做测 试设计时近乎完整的分支考量而更加能够兼顾所有的分支和异常。单元测试是一种高效的编码能力提升的手段,而在做单元测试的时候将分支的全面覆盖,则是高效 的设计技能的提升手段。
单元测试覆盖的纵与横的概念只是一家之言,无需纠结概念。我其实只是希望大家在考虑“覆盖率”这三个字之前都去考虑一下覆盖的策略,而不要吧眼 光只放在那些乏味的覆盖率统计数字上。做测试规划,好的方略比优秀的代码更加能解决实际问题;宁可让沙子漏得慢一点,也不要为了暂时看起来流得快而对沙子 中混入的石子视而不见,否则迟早会因为石子堵住瓶口而无法继续聚沙成塔的梦想。
耦合关系的处理
对于我们讨论的这种系统,有人说mock对单元测试来说不是银弹,滥用mock会影响交互点的验证,降低测试的有效性。对这种看法,我深以为然,而且单元测试且如此,通过GUI做的测试就更不用说了,盲目的解耦会大大提高为测试有效性所付出的代价。
拿我们一个较为单纯的系统为例,中间价是weblogic,数据库是oracle,部署逻辑关系如下:
1)Browser通过HTTP访问单点认证系统、用户管理系统、影像系统、打印平台与印章系统等;
2)Web服务通过TCP访问单点认证系统,通过LDAP访问OID,通过NFS访问NAS,通过T3访问App服务;
3)App服务通过JDBC访问数据库,通过T3访问用户管理系统、工作流、单证系统、后援集中录入系统和集团公网前置系统,通过LDAP访问OID,通过NFS访问NAS……
4)核心DB通过TJS/ETL/GoldenGate与集团其他应用数据库相连。
连同上文所述的系统群内部的业务逻辑和数据的共用,我们可以把我们的系统耦合分为两级:系统群内的耦合与平台级耦合。这两层耦合稍有区别,很多人 在通过GUI做自动化测试的时候,要么懒得去做mock工作,全部依赖所有的测试环境的稳定性;要么就花功夫把所有的关联系统全部mock掉。这两种做法 都是不妥当的,因为要指望所有环境都稳定无异于买彩票。另一方面,这些平台级的耦合关系,如果想通过mock的手法来解耦,将付出很大的代价,而程序的正 常功能也无法测试完整。例如,如果要绕过SSO和UM的认证,可能就要分析和运行时修改cookie,而这本身就是不安全的,也不是一定能够实现的。而 且,如果中间件的一个provider配置错误,绕过去的测试也不能发现这个问题,尤其是全部依赖自动化测试的情况下。
那么是否要在通过GUI去测试的时候始终保持所有这种平台级耦合的测试环境稳定呢?根据我们的经验,答案是肯定的,因为如果通过GUI去做测 试,这种耦合关系是保证被测功能完整性的基本条件。无论这些平台或系统有着多么频繁的构建与发布,在我们通过GUI去做测试的时候都要保持一个稳定可用的 版本,对于我们的系统的持续集成,这种需求更甚。我曾经说过:在通过GUI快速构建的时候要彻底mock掉对关联系统的依赖,这彻底二字其实并不贴切,我 本意所指只是系统群内的关联关系而已。而理论终归是理论,根据我们公司的实际流程,我又把这种系统群内耦合关系的mock分为两种:
自动化BVT/冒烟:在持续集成频繁的构建中,由于系统群内业务逻辑和数据的共用,关联系统的版本移交时间和频率不定,无法要求其保持稳定不变的版本以供关联测试,需要将这部分关联mock掉。
自动化回归测试:在同一个系统群内,所有版本发布的最终日期是一致的,故尔最终的回归测试都将在一个集中的时段内完成,而这段时间内基本所有的版本都已经趋于稳定。在这种状态下,出于测试全面和有效性的考量,自动化回归测试将不mock任何关联。
那么同一套测试脚本能否支持这种测试执行需求呢?我们借鉴了功能开关的特性,通过采集版本计划信息进行推算,做出一个判断是否回归测试的公共接 口。在测试脚本中调用共用接口,得到测试运行信息,决定如何处理对关联系统的依赖。持续集成对于很多公司或产品来说都不是件很难做的事情,而对于本文所述 的陈旧的紧耦合金融系统群来说却有一定的难度。我观察了我们公司一些持续集成做得比较成功的案例,大都是业务源头系统,如网销,基本不存在业务数据依赖 性,或是系统群内部关系简单或者索性就是独立不成群的系统。除了实施手段较为高明和付出的努力较多之外,最重要的是他们成功地规避了这里提到的紧耦合的问 题,或者根本就没有遇到这种问题。所以我个人的见解是:在通过GUI去做自动化测试的过程中,要确保平台级耦合系统环境的稳定性,分场景地mock系统群 内部的耦合关系,而不能一概而论。
监控辅助的测试
有人纠结测试自动化和自动化测试之间的差别,简单举个例子说明一下个人的理解:在测试环境搭建监控系统,用其辅助自动化测试运行,这个行为总的 来说可以称作测试自动化。而自动化测试主要的内容则是自动化(主要指脚本化)的测试执行动作,这二者之间还是有点差别的。如果非要咬文嚼字,我觉得其差异 在于自动化测试行为必定包含verifying,而测试自动化行为则可以只有甚至没有checking。如果将测试自动化的行为或体系在运用的时候加上 verifying,就可能变成自动化测试。
提到测试环境监控,我觉得它的价值绝不亚于生产环境的监控,它能够帮助测试节省很多问题定位的成本,帮助发现一些在页面上无法发现的异常。根据 个人理解,除去基础架构统一管理的相关内容,我将其划分为5个部分(目前并未完成建设):测试环境应用服务器监控、测试环境数据库状态监控、其他测试服务 的状态监控、自动化测试运行相关监控、业务系统逻辑健康度检查。用环境监控与自动化测试相互辅佐,可以发现更多的潜在问题,下面讲两个简单的例子来说一下 自动化测试和测试环境监控的关系。
例1、数据传输自动测试(未亲自实施)
很多时候,我们会使用几种商业工具和一些其他的企业级应用。在日常的测试工作中,针对这些工具或它们的特性也需要做大量的测试。例如,在平安科 技,单就数据传输管理而言,会用到:ESB/EAI/TJS、ETL(DataStage)、GoldenGate(Oracle)等。因为这些工具使用 的频率非常大,所以有想法的人就想着把这部分工作内容也做成自动化或者半自动化。
大家知道,这些数据传输的service或者job本身可能存在一定的加工和转换的逻辑(GoldenGate可能会较为单纯),自动化测试可能 需要对其内部逻辑做细致的测试。而对数据加工转换的逻辑做自动化测试,无异于换个人重写一套逻辑去验证原先开发的逻辑,十分复杂,ROI并不高。
这样一来,可能有人就把这个自动化变成了对这些service和job工作状态的checking,同时仍然简称这是在做自动化测试,并且试图 说明这样的测试通过就能够保证不出Level-1级别的故障事件。不过我认为,如果只是对这些service和job的基本可用性做checking,而 不是verifying,那么这部分自动化工作做成测试环境的一个监控即可,而不是自动化测试,而且这种checking是无法保证不出Level-1级 别事件的。
例2、报表生成自动测试(指导同事实施中)
我们的查询系统分三类:实时查询系统、综合报表系统和MIS系统,时效从高到低。实时查询系统自动化测试较为简单;而MIS系统由于维度和指标 非常复杂,暂时采用手动测试的方法。而这个综合报表系统有数百长单一维度的报表,每张报表使用一个独立的存储过程来生成,生成的结果文件含xls、 csv、zip、html等各多文件类型。报表之间的关联影响几乎可以忽略,而报表的正确性则会受其他公用逻辑改动的影响。此外,这种报表介于oltp和 olap之间,生成的效率从几秒到几小时不等,提交之后由quartz控制生成异步任务,是测试自动化的难题之一。
因为测试执行的机器资源有限,所以一般来说自动化测试的运行都是实时的,但是报表生成的时长却长短不一。理论上,如果不想占用太多资源,只能依 赖有轮询机制的工具或者系统来检查它运行的结果。恰好,我们的监控系统就有这种机制,而且既然监控系统能够检查报表是否已经生成完毕并触发邮件发送,它就 一定可以触发对其结果正确性校验的自动化测试程序的运行。我们可以考虑在监控平台配置一个新的监控和一组规则,随后实现这种报表系统的测试:
1)建立被测报表生成的存储过程代码的基线版本,存放于自动化测试的基线版本库中。
2)将自动化测试代码分为自动化报表生成和自动化报表结果校验两部分。
3)在任何变更引发的综合报表系统测试中,使用基线版本代码运行报表,取得报表结果文件;在最新的被测报表代码版本上,用自动化GUI测试再度生成这些报表,并取得结果文件。
4)使用监控系统轮询报表生成情况,已经完成的报表则调用其自动化结果校验的代码再度运行,解析并比对两次获得的结果文件,进行报告反馈。
5)被测报表逻辑发生变更,则更新被测报表基线代码。
很显然,这种系统的自动化测试,决不是单靠一段单纯的自动化测试代码的运行就能解决的,辅助的方法或许有很多,但是善用已有的资源,无疑是一个很好的办法。如上文所述,监控系统的checking行为由于它联动了自动化的verifying动作,就变成了自动化测试。
(未完待续)
由于最近一直比较忙,就没有更新博客。时间长了,一些领悟不记录下来就有些遗忘。闲话少说,今天来更新一篇关于自动化测试中的前后台交互方面的个人心得。
在自动化测试中,不论采用什么测试框架,基本的思路都是前台由测试人员填写测试用例,然后生成测试脚本,然后使用测试工具去run这些脚本。而后台呢,往往是由测试开发人员根据被测对象的需要自己编写程序。至于测试方法,可以将前后台分离进行测试,比如说,对后台进行测试,然后判断后台是否满足测试需求。如果前台测试的目标不需要调用数据,直接run脚本就OK了,如果前台需要后台数据,可以在后台准备好数据,可以将数据放在log日志中,前台直接调用log日志,不需要在后台执行后台程序。这样就保证了前后台测试的分离,便于测试维护,例如前台如果经常变动的时候,只需要根据前台变动将相应的后台数据准备在前台要调用的数据池中。而由于后台程序一般的变动会相应的较少,这样不需要每次更改前台界面都要把后台测试程序进行修改。这种方法也是上次谷歌中国的测试总监推荐的方法。
目前的实践还没有做到完全的前后台测试分离,只是在一步步的尝试,希望距离前后台分离的目的越来越近。对于前后台的交互,目前我的做法是。前台web界面测试由测试人员根据测试对象填写由我们测试开发提供的测试用例模板,然后生成自动化测试脚本。前台界面的一些判断可以直接得到,例如界面的一些改动,界面元素的缺失等。而对于后台的一些验证,目前并没有在后台单独的进行测试。而是由前台的脚本通过调用cgi,将测试预期传给后台,后台cgi函数处理预期值,然后跟实际值进行比对,然后将比对的结果再传给前台由前台处理。举个例子,比如说我的被测对象是一个具有web界面,功能是对交换机进行端口切换的系统。这里,如果我对端口切换这个功能要进行自动化测试设计。那么我的测试工具前台就应该具有驱动界面操作的测试环境,测试后台是一些cgi程序,当前台执行的脚本将端口切换的预期值通过cgi传递给后台时,后台测试程序首先会telnet交换机,获得交换机的当前端口信息,然后与前台传递的预期值进行判断,将判断结果返回给前台日志。
由此可见,与前后台完全分离的测试方法相比,目前我们的做法优点是不需要独立的后台测试,而是从系统测试的角度来判断系统功能。如果要做前后台测试分离,那么前台测试就只对显示的正确与否进行测试,可以在后台数据池准备相应的数据,让前台去访问这些数据然后输出界面,观察输出的数据是否是数据池中的。而后台就对功能进行验证,将功能的结果放于数据池中。这方面目前还没有做尝试,有兴趣的话可以试试。
从管理人员到开发者,每个人都在说单元测试,但是却很少有人执行。有关单元测试的好处相信大家也能例举出一二,但很多时候,开发者面对自己的项目代码却无从下手。
Lurkerbelow在公司里是唯一执行单元测试的一名开发者,他深知单元测试带来的好处,也积极提倡单元测试。他甚至与公司的管理层人员、开发者都讨论过单元测试,但却无人对此感兴趣。 为了与开发人员形成一条战线,Lurkerbelow甚至“被迫“提交了代码审查(Gerrit)和持续集成开发(Jenkins)。
无奈之下,Lurkerbelow在 Stack Exchange发出上“求救”,抛出《“如何激励同事进行单元测试?》的话题,引发了众多开发者的关注,纷纷献策。
对此,我们从中摘译了几个较为重要的观点与大家分享,希望能引起大家的共鸣。
实质的文档或许有帮助
jimmy_keen:我注意到几乎很少有公司在谈论TDD。人们更乐意看到最终结果。人人都说“编写单元测试将缩短开发时间”,这是似乎是真的,但这并不足以让人们相信。
我与你处在相同的位置(但不像你这么糟糕),开发者在代码问题上都能够自行解决(这里的代码是指单元测试)。某个项目停止更新时,本地的调查自然就会更进,进而找出问题所在。
然后,当我们进行单元测试时,如果测试被通过了,大多数问题会出现在最新的、未测试的代码中。如果不是,测试通常能够发现问题(至少找出了正确的方向)。我们修复Bug,再进行测试。
一句话,如果发生类似这样的情况,将会有超过2名开发者变成可TDD 测试爱好者(我们希望更多人参与)。
建议,你可以选择TDDkata将使用测试作为首选方法。
根据任务的复杂程度,非测试方法进程通常较为缓慢,尤其是当增量编码器需求发生更改时。
● Roy's string calculator
● Bank OCR
找出问题所在,“对症下药”
HLGEM:首先,要弄清楚为什么他们不喜欢写单元测试。 通常严格的时间进程表是导致其最大的原因。
其次,现有的大型未测试的代码基,编写单元测试工作量巨大。因此,开发者本能认为:“这太麻烦了,我得跳过去。”
另一个原因可能是,他们骨子里认为测试是个好方法,但他们在如何写测试上没有信心,尤其是他们从未接触过。究其根本原因,是开发者根本不会写单元测试!
还有一大原因是,他们没有看到这项额外的工作所带来的好处(利润)故放弃,即便是他们想提供这样的服务。
那么,对于以上这些情况该如何处理呢?
Reason 1:向开发者展示案例,如何节省开发时间。
Reason 2:告诉开发者在一年内能编写多少测试,代码基覆盖了多少比例。
算算这一年里他们写了多少测试,明年他们依然愿意这么做。一旦他们发觉每天都会进步一点点,思想上就会潜移默化了,从而产生质的变化。
如果可以的话,把系统数据拉出来,让他们知道在未经测试的代码中有多少重复Bug?进行单元测试的代码中又有多少重复bug?
Reason 3:培训,让开发者在培训班中编写测试。
Reason 4:这是问题的关键所在,首先,选择一个痛点,比如在某个项目中这些Bug被多次返回。在上述过程中,向管理部门提出建议,如果他们在这个项目中进行单元测试,那就不会出现不想见而又偏又见到的代码。
当然,作为开发人员,我们首先要学会自我管理。
写好单元测试,学会重构很重要
ElYusubov:我想先说说TDD的好处。
从正常人类的角度思考,开发者都是以利益为主,因为他们不想进行工作意外的事情。单元测试意味着更少的工作;意味着与朋友相处的时间更多;意味着有更多的乐趣,因为你无须每个夜晚编码工作到11点;也就意味着可以舒心的度过假期。
想要写好单元测试,学会重构是很重要的。这里补充几点:
1、编写测试代码建立基本的防护网;
2、在单元测试和功能测试之间要有取舍,如果单元测试实施成本很高,可以先加功能测试;
3、通过增加中间层来打破依赖,不是为了去掉依赖,而是为了后续的修改以及测试的便利;
4、将第一步中编写的功能测试换成单元测试。
TDD最大的好处之一是,你可以重构程序获得更好的设计或者只需改变某个项目的名称……只要这种设计没有破坏测试,前提是你有100%的信心保证你的改变没有破坏任何东西。
TDD为遗留代码创建单元测试,这将出现重构。从长远的来说,这将有有助于改善你的代码基础知识,了解其优缺点以及代码中现有的的硬编码业务模块,为你提供一个良好的开端,为提高产品质量向前迈进。
事务(Transaction)用于模拟用户的一个相对完整的、有意义的业务操作过程,例如登录、查询、交易、转账,这些都可以作为事务,而一般不会把每次HTTP请求作为一个事务。
拿笔者所测试的邮箱系统为例,对于邮箱来说更基本且用户使用最多的应用就是收发邮件功能,我们可以拿发邮件看作一个事务:打开写信页,填写收件人,主题,内容,点击发信。在做性能需求分析的时候也就是把系统的业务抽离出来,在性能脚本中用事务来描述。
我们在录制脚本的过程中,可以通过录制面板的事务按钮来添加事务。
同样以loadrunner 自带WebTours为例,操作步骤:
1、打WebTours首页
2、点击事务开始按钮,输入“登录”
3、输入用户名密码点击登录按钮
4、点击事务结束按钮,确定。(注意:事务的开始与结束的名称一定要一致)
脚本如下:
Action() { web_url("WebTours", "URL=http://127.0.0.1:2080/WebTours/", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t3.inf", "Mode=HTML", EXTRARES, "Url=../favicon.ico", "Referer=", ENDITEM, LAST); lr_start_transaction("登陆"); //事务开始函数 web_submit_data("login.pl", "Action=http://127.0.0.1:2080/WebTours/login.pl", "Method=POST", "Referer=http://127.0.0.1:2080/WebTours/nav.pl?in=home", "Mode=HTML", ITEMDATA, "Name=userSession", "Value=110409.949777238fzHQcVVpHQVzzzzHDtAHfpcHAHf", ENDITEM, "Name=username", "Value=test", ENDITEM, "Name=password", "Value=123456", ENDITEM, "Name=JSFormSubmit", "Value=on", ENDITEM, LAST); web_submit_data("login.pl_2", "Action=http://127.0.0.1:2080/WebTours/login.pl", "Method=POST", "RecContentType=text/html", "Referer=http://127.0.0.1:2080/WebTours/nav.pl?in=home", "Snapshot=t4.inf", "Mode=HTML", ITEMDATA, "Name=userSession", "Value=110409.949777238fzHQcVVpHQVzzzzHDtAHfpcHAHf", ENDITEM, "Name=username", "Value=test", ENDITEM, "Name=password", "Value=123456", ENDITEM, "Name=JSFormSubmit", "Value=on", ENDITEM, "Name=login.x", "Value=51", ENDITEM, "Name=login.y", "Value=12", ENDITEM, LAST); lr_end_transaction("登陆",LR_AUTO); //事务结束函数 return 0; } |
查看运行结果:
Action.c(15): Notify: Transaction "登陆" started. Action.c(17): web_submit_data("login.pl") was successful, 795 body bytes, 225 header bytes [MsgId: MMSG-26386] Action.c(29): web_submit_data("login.pl_2") was successful, 795 body bytes, 225 header bytes [MsgId: MMSG-26386] Action.c(45): Notify: Transaction "登陆" ended with "Pass" status (Duration: 0.5704 Wasted Time: 0.0152). |
通过运行结果我们可以很清晰地看到本次事务是pass的,整个脚本的持续时间和事务的消耗时间等。
插入事务的方法:
插入事务操作可以在录制脚本过程中,也可以在录制结束后进行。可以在脚本中找到需要添加事务的部分,直接插入:
-------------------------------------------------
lr_start_transaction("事务");
.....
lr_end_transaction("事务",LR_AUTO);
------------------------------------------------
注意:1、开始与结束函数必须成对出现
2、事务的名称必须一样。
当然,我们也可以将鼠标定位到要插入事务的位置,通过菜单栏来插入事务(insert--->start transaction/end transaction)
---------------------------------------------------------------------------
到些,loadrunner 脚本的一些基本设置已经介绍完了。
相关链接:
LoadRunner 技巧之协议分析
LoadRunner 技巧之THML 与 URL两种录制模式分析
LoadRunner 技巧之思考时间设置
LoadRunner 技巧之集合点设置
LoadRunner 技巧之自动关联
LoadRunner 技巧之手动关联与预关联
LoadRunner 技巧之检查点