第2章 终极数据库自动化
Henri van de Scheur
Henri van de Scheur讲述了一个跨越6年的故事,是有关他和他的同事在开发一款适用于多个环境的测试数据库工具时所发生的事情。他们为自己的自动化测试设定了良好的目标,并为测试工具搭建了一个良好的架构。他们使很多测试实现了自动化,从而为自动化测试构建了一个完整的生命周期,包括周期性的bug清理。测试是每晚、每周运行的,或者根据特殊的时间表来安排执行。虽然取得了巨大的成功,但他们也遭遇过一系列的问题,Henri都如实地进行了描述。这个数据库测试工具(现在是开源的)是由一支小团队短短几年内在挪威开发的,并且取得了非常可观的投资回报率(ROI),如表2-1所示。
表2-1 案例研究特征
2.1 本案例研究的背景
这个案例研究描述了我(指Henri)在一个快速成长的新公司里的经历:刚开始的时候,大约有50个员工,但仅仅过了五六个月,员工数量就增加到超过100人。由于人员数量增加太迅速,导致后来加入的很多开发人员和测试人员缺乏对产品的基本了解。知识的交流被极大地忽视了。结果,产品和测试的质量都很差。
办公室里有大约50 ~ 60个开发人员及20个专用测试人员,分别分布在同一办公大楼的各个楼层。测试人员报告产品中存在的问题,但由于测试质量差以及缺乏对产品的充分了解,以致这些问题往往被忽视。通过改善开发人员和测试人员两个群体间的交流,开发和测试工作都从中受益并且质量都得到了提高。所以,良好的交流是开始考虑自动化测试的先决条件。
【真知灼见】
不要尝试对设计很差的测试实施自动化,先完善测试,再进行自动化。
一个包含大约30 ~ 40个测试的典型的发布测试只在一个平台上运行,这就可能需要15 ~ 20个测试人员花费3 ~ 4周的时间。同时,由于发布测试中有更多的bug修复周期和测试周期(一般是4 ~ 6个周期),因此,在可以发布一个产品的新版本之前,过去需要大量的时间。由此可见,将以上过程实现自动化的需求是非常显著的:周期必须缩短,人力资源必须减少,并且测试人员必须在多个平台上运行测试。
一开始,我们使用Java语言开发了一个能够满足以上需求的内部工具,随后再将更多的新功能逐渐添加进去。到目前为止,这个工具的一个更新的版本也已经开发出来了,且又增加了一些新功能。测试也是用Java语言开发的。虽然自动化测试是从GUI自动化开始的,但是基于命令行界面的测试也变得日益重要,因为它可以使团队更方便地通过已安排的分工协作进行自动化。
虽然当时有一些现成的测试套件可用于测试数据库,但是我们并不知道使用什么自动化工具来进行数据库的测试。另外,我们的数据库有当时市场上还没出现的一些特殊功能,而这是现有的测试工具无法测试的。虽然当时已经开发出了一款内部测试工具,但是它根本没法满足我们的需求。那时,我们突然有一些可用资源来开始这样的一个项目,基于这些原因,我们进一步开始测试工具的开发。
2.2 测试中的软件
该项目中要测试的软件是比较特殊的,因为它仅仅只包含数据库。虽然有一些现存的测试套件可用于测试数据库,例如测试多个数据库API和查询语言之间兼容性的测试套件,包括JDK(Java Development Kit)、JDBC、ODBC(Open DataBase Connectivity)和SQL,但是这些工具的使用并不广泛,并且(或者)它们仅仅只是为使用它们的数据库量身定做的。所以本案例研究中的测试和测试工具都是内部开发的。
我们定义了一个操作系统组合而成的平台,包括它的品牌和JDK版本。我们后来又将另一项包含进去:平台是32位还是64位的。
例如,包括以下几项:
Solaris 10 SPARC, JDK 6, 32位;
Solaris 10×64, JDK 6, 64位;
Solaris 10×64, JDK 6, 32位;
Solaris 10×86, JDK 6, 32位;
Solaris 10×86, JDK 5, 32位;
Windows Vista 64, JDK 6, 64位。
2.3 自动化测试的目标
在《Software Test Automation》(Addison-Wesley,1999)一书中,第8章有一张非常有用的表,列出了自动化测试的不同目标。当我们开始自动化测试时,根据这张表,按照优先级顺序,列出下列这些测试目标:
增强在软件质量方面的信心;
更早进入市场;
减少测试开销;
保持可重复性测试的一致性;
自动运行测试;
找出回归测试中的bug;
经常运行测试。
第一阶段实施完成之后,又加入了以下测试目标:
质量更好的软件;
测试更多的软件;
性能测量;
找出更多的bug;
在不同的操作系统上测试。
【真知灼见】
刚开始不要设定太多目标,最初先重点完成某些目标,再逐步添加新的目标。
另外,在更深入的自动化的全新迭代周期中,我们又制订了新的目标:
1)收集统计数据来制定度量标准(用以回答以下问题):
① 在哪个操作系统上找到的bug数量最多?
② 在同一个操作系统上将测试进行分割会不会漏掉一些bug?
③ 哪些bug是在特定操作系统上才出现的?是在哪种操作系统上出现?
④ 哪些测试发现的故障数量最多?
⑤ 哪些测试从来都没有发现故障?
⑥ 哪些失效是由发布测试发现的,而不是由每晚的测试所发现的?这有利于我们分析为什么这类故障在发布过程中比较晚时才发现。
2)将未使用的硬件用来做其他的事情。
3)尽量达到每周7天、每天24小时不间断使用硬件。
4)缩短bug从发现到反馈给相关人员的时间间隔(最初从几周缩短到几天,然后缩短到只隔一夜,最后缩短到检查代码之后就直接完成反馈。)
5)使用我们自己的数据库来收集统计数据,这样就可以在真实的产品环境中拥有自己的数据,并且有可能会遇到在其他没有发现的故障(用你自己的方法来解决)。
6)使测试场景不可能手动运行。
7)使场景维持几天。
8)使场景具有多个用户。
9)重新使用预处理任务,并使其自动化。
10)重新使用后续处理任务,并使其自动化。
11)对于测试结果自动生成测试报告。
12)完全的自动化测试。
2.4 开发内部测试工具
该内部测试工具的基本功能是由3 ~ 4位开发人员在6 ~ 9个月的时间内开发出来的,是用Java语言编写的。第一个版本开发之后,一个人专门负责对其进行维护和进一步的开发,显然维护和进一步开发的工作量是逐步减少的。
图2-1是测试的Java引擎(Java Engine for Testing, JET)架构的一个概览。每个大的矩形都是一台运行某些软件的计算机。
我们在图2-1中的运行机(runner)处开始运行一组测试集合。它使用JETBatch来开始运行测试并收集运行结果。客户端(client)运行与JETBatch进行交互的Jet代理程序(JET Agent,JAG),即使用JAG来启动JET运行单个测试。这些JET读入一个XML文档,该文档会给JET提供测试运行的内容,并与服务器的JAG进行交互来启动我们需要测试的软件。整套系统还包括一个测试装置,通过使用不同的机器操作不同的任务,避免了架构本身的资源消耗对被测任务的影响。
因为我们确实投入了大量的时间和精力来设计自动化测试,所以几乎实现了所有的目标(只有性能测试是由我们的另一款内部开发工具完成的,这不在本案例研究的讨论范畴)。几乎所有的测试都是通过我们的工具自动运行的,其中只有几个测试,我们认为自动化的价值并不是很大,是由手动完成的。最后,我们有了一款可以在很多不同领域使用的、功能非常强大的工具。
图2-1 测试工具的架构
2.4.1 配置
测试配置:我们的测试是定义在一个数据库中的,并且可以单选、成组选择或者根据特定序列来进行选择。工具在每次测试之前都会进行一次初始化,避免前面的测试结果影响到后续的测试,工具在每次测试之后也会将测试环境清理干净。工具还自动将测试件进行收集和归档。
【小窍门】
把实施预处理和后续处理作为启动一个测试套件的一部分。
测试时应用程序的配置:可以对产品版本进行选择,包括调试版本和来自开发人员“沙箱”(sandbox)的本地版本。
服务器和客户端平台的配置:我们使平台的定义和在运行测试的平台组的定义变得简单。根据测试装置在一个平台组(例如,Windows Vista,64位,JDK 6)里面是否可用而将其划分成不同的测试装置。我们可以用一到两个平台为服务器设置一个单独的配置,通常也可以用一个平台为客户端设置一个单独的配置。对于客户端和服务器来说,都可以选择不同的操作系统。
一个标准的测试需要4台机器:一台测试机器,一台客户机,两台包含被测数据库的服务器。
2.4.2 资源优化
通过添加更多的机器到测试装置池中,我们可以并行地运行测试。另外,测试具有排队机制:一个测试装置上的测试完成之后,候选队列里面的下一个测试就开始运行。
2.4.3 测试报告
这个内部工具创建了网站来记录测试报告,所有的结果在一个数据库中也进行了详细存档,这有利于我们建立详细的度量,比如下面的度量:
1)在哪些平台上会有一些什么样的bug及其出现的频率(可以帮助指定bug的优先级)。
2)每个平台上的一般信息统计。
3)测试中bug的检出率。
4)测试的冗余。
一个测试完成之后,会自动发送一个包含测试结果的汇总邮件,同时生成一个XML文件,它包含了用以导入到其他数据库或者报表生成器的所有必要信息。
该工具也使得从开源测试覆盖工具(EMMA)中导入信息变得更容易,而且让我们能观察到测试的质量——至少从表面上看是这样的。
2.4.4 故障分析
在测试失效分析达到60% ~ 80%正确率之后,才能对失效进行自动识别。比如,通过定义描述故障的模式和标记来进行识别,在大多数情况下,我们定义一些模式或者签名对故障进行描述,这些模式或者签名通常与测试产生的错误信息、测试名称和产生这条实效信息的测试语句直接相关。一个bug可能有多个标记,如果要对新的故障或者已知故障的新症状进一步进行人工分析,就需要新的标记信息。
用这个新工具实施的某产品的首次发布测试中,要求不论何种原因,无论是产品原因或者是测试原因,至少75%的测试运行的时候不会出现故障。最后,要求至少96%的测试运行的时候不会出现故障。
2.5 结果
该工具经过3年的开发和使用,在6 ~ 10个平台上有200个测试的发布测试,可以仅由一个人在3 ~ 4天内运行完成。这等价于(在实施自动化之前)在16天的时间里(16:4)20个人(20:1)在一个平台上(6:1)运行了40个测试(200:40)。所以自动化测试过程帮助我们提高了至少2400倍的工作效率!
【真知灼见】
用一种最恰当的方式向希望知道结果的人们来表述你的成功(这里的表述是,“将效率提高了2400倍”)。
现在所有的测试人员都可以集中精力进行测试的开发和进一步的工具开发,跟刚开始需要进行重复性的工作相比,他们现在的工作也不会那么枯燥了。我们的产品和测试的质量都得到了极大的提高。开发人员和测试人员都得到了应有的相互尊重,并且他们互相通过挑战来激励自己,这样使他们工作的兴趣更浓。
通过实施自动化,维护工作只占测试人员整个工作量的不到1/10,而且比之前的工作量要低很多,部分原因是因为产品进一步开发中的其中一个需求是后向兼容的。这是一个罕见的机会,测试与测试工具本身因为新的功能而需要改变。另一方面,新的功能通常需要新的测试,在某些情形下用以替代旧的测试。
(未完待续...)
相关链接:
自动化测试最佳实践 连载一
自动化测试最佳实践 连载二
自动化测试最佳实践 连载三
自动化测试最佳实践 连载四
1.5 使用增量方法
与很多团队一样,我们发现在每两周的迭代后期都有未完成的测试任务。有时候用户故事未完成。比如,我们从一个包含5页向导程序的UI开始一个故事(story),其中只有4页完成了。
有一位程序员提议用一个“强线程”(steel thread)来标识复杂故事——一个将功能点从不同终端隔离开来的小的功能点。我们为它编写测试、写出代码,然后将测试自动化然后转移到下一个线程。那样的话,测试自动化即便在GUI层面,也能与开发保持一致。第一个自动化的测试可能会过于简单,但可以逐步进行补充。
【小窍门】
强线程是一种保证自动化在时间里程碑内完成的方法。
这种方法很有效,对于每一个计划的复合功能点,我们首先将线程画在白板上,并确保在转移到下个线程之前,当前线程的自动化测试及手动测试都已经完成了。
1.6 正确度量
如何知道我们是否取得进步了呢?如何衡量成功?我们需要一些度量。我们的构建过程会对测试进行统计,所以很容易跟踪每一个层次的测试数量:JUnit、FitNesse断言和测试页面、Cannoo WebTest断言等。
这些原始数据并不能代表整个趋势。我们,包括业务经理,都希望看到这些数字在每次迭代中都在增长。在每一次迭代中,指定一份染色的日历——如果某天所有的回归测试至少通过了一次,则将其日期标记为绿色,否则标记为红色。业务人员也非常关注这些数据,如果他们在一行里面看到两个标记为红色的日期,他们会向我们询问原因。他们也会记录每一次迭代中JUnit、FitNesse和Canoo WebTest的测试数量。当测试数量减少时,他们也会关注并询问原因。
【真知灼见】
让经理看到自动化测试的好处。你需要不断“推销”自动化的好处。
让测试结果对外可见也是一种宣传的形式,可在整个公司范围内加强公众对自动化回归测试的了解。客户看到测试数量增加,并且发布的功能点越来越多。所以,当我们申请时间来重构产品或者改善构建过程时,我们的客户就能够理解了。
我们需要快速的反馈,所以每一次构建花费的时间是一种重要的度量。如果运行JUnit测试的持续构建花费的时间超过10分钟,就开始备份检入信息,那么就很难找出是哪一个测试导致了失效。当JUnit构建花费的时间超过10分钟时,我们停下来查看是什么原因导致了运行缓慢。我们重构测试、添加硬件、升级操作系统、配置测试使其并发运行等,这些都会使反馈循环更快。对运行更高级测试的构建也是这么做的。如果FitNesse测试要花好几个小时来运行,那么反馈太慢。我们要在测试覆盖和速度之间权衡。幸运的是,虚拟机可以同时运行多个测试套件,减少反馈周期并且非常廉价。
【小窍门】
不要使自动化停滞不前:在需要的时候进行重构、使用新的硬件、重组测试。记住我们的目标(这里指更快速的反馈),并修改自动化测试来达到我们的目标。
1.7 庆祝成功
100个JUnit测试并不是很多,但每一个测试代表了许多断言,并且是一个值得庆祝的里程碑。当达到1000个JUnit测试时,我们觉得这值得全公司聚会庆祝一下,并为全公司的人购买了比萨。当庆祝第2000个JUnit测试时,我们组织了一个有饮料和拼盘的小型聚餐。有人曾经问过我,这些庆祝会不会鼓励一种不好的事情:盲目增加不必要的测试数量。其实,因为每个测试都是刚好在让其通过的那部分代码之前编写的,所以我们没有进行任何无关的单元测试。如果包含测试的代码移除了,那么测试也会移除。
当接近3000个JUint测试时,敏捷教练让我撰写一份说明书,谈谈在单元测试级别中一个健壮的回归测试套件对业务的重要意义。我写了一份关于TDD的高级描述报告,即它是如何帮助我们开发出健壮的、易维护的代码,并在单元测试级别为我们提供了回归测试覆盖的安全网。我试图阐明JUnit的目的:帮助设计代码。也就是说,我们不是胡乱编写单元测试的。敏捷教练把这份报告发给公司的每个人,并在一家本地饭店预订了一个聚会。不仅是为了庆祝我们完成任务,而且让所有的业务利益相关者也能体会其中的意义。
1.8 引入工程冲刺
因为我们已经花时间向业务经理解释了测试的整个过程和实践,所以他们了解了技术债务的观念。每当我们为了顾及截止日期而走捷径的时候,代码就变得很难处理。如果我们没有时间将工具升级到最新版本,或者没有时间尝试能让我们更高效地工作的最新软件,我们的速度就会下降。这就好比如果我们拖欠信用卡债务,利率就会上升,我们所欠的债务就越来越多。如果我们被技术债务缠身,就无法以客户期待的速率来发布大量的软件。
所以,我们每6个月会进行一次特别的迭代,称为“工程冲刺”在这次迭代中不需要发布任何新的功能点(engineering sprint)。我们可以利用这段时间完成以下工作:升级到最新的Java版本、进行大型重构、尝试新的测试工具、重构FitNesse测试来消除冗余。在一次工程冲刺中,我们将源代码版本控制系统从CVS转变为SVN(Subversion)。在另外一次工程冲刺中,我们将构建过程从CruiseControl转变为Hudson,从而为缩短反馈周期提供了更多的选择。每当在更短的时间内发布了更多更好的功能点时,业务人员就可以看到这些投资的回报。
1.9 团队成功
我们的团队在一年的时间内,从没有自动化测试发展到将所有的回归测试进行自动化。我们的自动化金字塔还不完全是理想的形状,但是我们有了好的测试框架和在每个级别(见图1-2)实现测试的驱动程序。尽管这样,我们却没有满足现状,而是继续寻找在各个级别有效地进行自动化回归测试的方法。当我们可以控制功能测试的自动化之后,我们将着手性能测试的自动化。我们有处理不完的挑战!
最好的是,我们有时间对每个用户故事或功能集合进行充分的探索性测试,这样的话,在产品中就很少会有问题浮出水面。同时自动化测试也可以帮助我们进行探索性测试。我们希望创建:使用Ruby的具有高灵活性的脚本,Watir(Ruby中的Web应用测试)测试驱动,以及测试/单元框架。它们接收运行参数,允许我们在几秒或者几分钟内建立复杂的测试数据和情景,而不再需要花费时间搭建繁琐的手动测试平台。通过脚本可以直接对需要进行测试的部分进行测试,这样我们就可以拥有更多的时间来深入探索软件。
【真知灼见】
使用自动化测试来支持创新型手动测试。
我们的目标是使所有回归测试实现自动化,但是这一目标有几点需要说明。要达到一个合理的ROI,自动化测试在设计时必须要考虑到长期可维护性。因此要求程序员拥有好的设计技巧,可以帮助设计各个级别的自动化测试。我们不断对测试进行重构以使维护费用较低。同时,在选择将哪些测试保留在自动化回归测试套件中时,团队要有准确的判断力,需要“正好”(good enough)覆盖。测试太多意味着反馈周期太长和维护费用过高。同时,我们还有少量的遗留代码没有被自动化测试覆盖,当我们进行可能对遗留代码造成影响的大范围的代码重构时,我们要留一些时间进行手动回归测试。
图1-2 自动测试金字塔使用的工具
我们成功的关键是采用“完整团队”(whole-team)的方法,团队里的每个人都致力于将所有回归测试实现自动化,我们有很多的技术和观点来帮助解决自动化中遇到的问题。自动化测试金字塔的每一层都包含不同的工具,而且大家可以关注不同层。开发人员像写代码那样编写单元测试用例,测试人员更多的是进行GUI测试的自动化,在FitNesse中,双方在中间层合作。然而,在每一个用户故事完成之前,团队中的每个人都负责每个用户故事的所有测试活动。
自动化测试给我们一个快速的反馈周期。如果在几小时内任何一个现有的功能点遭到破坏,在工程冲刺时,我们应该腾出时间来做出改变,使加入更多测试用例后的反馈周期仍然很快。我们知道是代码的哪些改变引起的这个问题,并立刻进行修复。我们可以在经常发布新业务价值的同时,达到公司的要求,开发出最高质量的产品。
1.10 持续改进
2011年是我们自动化测试之旅的第8年,总是要面临新的挑战。正如本章所述,我们的GUI测试套件已经增长到需要2个多小时来运行。这个时间太长了,所以我们将它划分为两个测试套件,并在两个从属机器上并行运行。这需要进行大量的工作,因为有些测试依赖于其他测试,过去没有好好实施而是采取了折中的方法,现在要为此付出代价。我们有超过5400(这个数字还在增长)个JUnit,并且重构的FitNesse测试套件在30分钟内完成。
我们知道在单元级别中的测试覆盖率,但是并不知道在功能性或GUI级别的测试覆盖率。我们目前采用一个的工具进行实验,可以测量出FitNesse测试的代码覆盖率。
相比于8年前,我们现在对自动化测试设计有了更深入的了解,并且我们的测试需要大规模的重构。无论何时,当加入一个测试用例或者对其进行修改的时候,我们都尝试去改善测试,但是只在工程冲刺阶段才进行大规模重构。我们通过一些开源工具进行观测,并不断尝试我们认为可以降低测试维护费用或者提供更快反馈的新工具。
在GUI测试中我们也是这么做的。几年前,当我们发现Canoo WebTest并不能很好地支持JavaScript的时候,就在所有回归测试中开始使用Watir编写新的功能点。约一年之后,Canoo WebTest升级到比那时的Watir可以更好地处理JavaScript和Ajax,而Watir更难与构建过程整合,所以在保留现有的Watir回归测试脚本的基础上,我们又切换回WebTest。
我们也进行了调查,准备使用Slim来替代Fit进行我们的FitNesse测试。同样,我们可能不会立刻把所有FitNesse测试转为Slim,但我们同时发现维护多种工具也没有很大问题。
这些年来,我们的员工相当稳定。一位新程序员和一位测试人员来了又走,但是团队的核心成员一直保留着。我认为这是因为优秀的人才乐意留在他们可以尽最大努力工作的地方,那就是我们团队。比如,我们允许他们做类似自动化回归测试的事情。我们有一些有趣的转变,一位测试人员决定成为一位程序员,所以他参加了一些Java课程的培训,另一位程序员跟他一起合作,他的学习速度非常快。同时,他仍然保留了自己在测试方面的兴趣,尤其在性能测试方面。另一位程序员一直都无法采用TDD方法,尽管经过了大量的指导和培训,因为他的代码始终没有达到标准,最终只好让他离开。我们从不会去刻意填补空缺职位,但我们的团队效率却在提高。每一个团队都需要恰当的人选,而且那些人需要时间来学习、实验和提高。
1.11 总结
你的团队如何?阻碍你进行自动化测试尝试的最大问题是什么?你的团队是否缺乏一个特殊的技能组合?你是否只是需要时间来制定和实施策略?你是否还在等待合适的硬件或者软件?想想你现在能做一些什么事情来提高自动化测试并缩短反馈周期。要有耐心,一步步慢慢来。对你得到的结果进行试验和分析评估,不断进行哪怕只是很小的改进。不知不觉间,你就会享受自动化测试的好处。
(未完待续...)
相关链接:
自动化测试最佳实践 连载一
自动化测试最佳实践 连载二
自动化测试最佳实践 连载三
一些测试技术,比如边界值分析和结对测试,可以有效的帮助我们在尽量少的增加风险的同时,减少测试用例的数目。然而常见的问题在于产品的缺陷并不是 平均分布在代码里面的。在一些典型的软件项目中,总有一些组件比其他组件存在更多的缺陷,软件测试一个非常必要的环节就是预测那个项目区域存在更多的缺 陷,并有针对行的投入测试力量。
1、风险行业
基于风险的测试就是基于缓解产品中潜在风险的测试方法。该方法倾向于把可用的测试资源集中在最需要的区域。无法做到“面面俱到”的测试,我们就需要基于一系列的标准,有选择地集中投入测试力量。
基于风险测试的方法是尝试归类出部分产品组建拥有更普及的用户场景,从而投入更多的测试力量。但这种测试方法的风险依赖于对测试重点的精确选择,从而忽略了一个事实,还是有用户会使用那些20%以为的功能和代码的。
对最有可能产生缺陷的产品部分编写更多的测试用例是基于风险测试的另一种应用。
2、复杂问题
我们用一种寻找高复杂度代码的方法来预测缺陷出现的地方,代码审查。
作粗略的代码审查时评感觉和主观衡量代码复杂度有时就足够了。
最简单的代码复杂度测量方式可能是代码行数(LOC),行数越多,复杂度就越高。但是判断行数,没有统一的标准,个人有个人判断的方法,我倾向于只计算语句(C语言为例,只计入以分号结束的行)。你只需挑选你喜欢的即可以。
3、测试回路复杂度
所谓回路也就是程序中选择和判断的语句。回路复杂度度量方法,即辨别函数中线性独立路径(或判断)数目的度量方法。一个没有包含条件判断操作(比如条件语句,循环等)的函数在整个程序中只有一条线性独立的路径。
计算McCabe复杂度=边-节点+2,还有一个最简单的方法:将条件(判断)语句的数量加1.
3.1 Halstead度量
Halstead度量是一套完全不同的复杂度度量,基于程序中语法要素以下4个度量:
● 独特算子的数量(n1)
● 独特算域的数据(n2)
● 所有算子出现的总数(N1)
● 所有算域出现的总数(N2)
代码长度值=N1+N2;难度度量=(n1/2)*(N2/n2);
void HalsteadSample(int value) { if (value != 0) { if (value < 0 ) value + = 1; else { if (value == 99) value = 0; else value - = 1; } } } |
算子 数量 算域 数量
!= 1 value 6
< 1 0 3
+= 1 1 2
== 1 999 1
= 3
-= 1
总计:n1=6,N1=8,n2=4,N2=12
函数长度量=(N1+N2)=18,难度系数(n1/2)*(N2/n2))=9
运用这种度量只要是用来标识可能需要返工或是额外分析的代码
3.2 面向对象的度量
面向对象的度量是各种语言中类和类结构相关的度量(Java、C++、C#),该方法即度量。该方法包含以下内容:
1)每个类的权重方法(WMC)。一个类中方法的数目。
2)继承树的深度(DIT)。一个类所继承的类的数目。
3)对象类之间的耦合(CBO)。一个类引用其他类的方法或是实例变量的数目。
在面向对象的编程中,扇入和扇出度量分别用来计算有多少类调用到某一特定的类,有多少类被某一特定的类调用。例如一个类包含的方法被其他5个类调 用,但是这个方法同时调用了其他10个类,那么它的扇入就是5,扇出就是10。那么也就是说,如果修改了扇出中的某个类,同时就要测试扇入5个类的地方(这样说不是很确切,但意思就是这么回事)。
4、如何利用复杂度度量
同时使用集中方法检查函数的复杂度,通过结合和权衡这些数据来减少误诊
函数名 | 回路复杂度 | 调入函数的数量 | 代码行数 |
OperAccount | 21 | 3 | 42 |
CloseAccount | 9 | 24 | 35 |
updatepassword | 17 | 18 | 113 |
从中可以看出,OperAccount函数回路复杂度很高,但调入函数却很少(扇入),代码函数也很少,所有这个函数只需通过减少回路复杂度(分支语句)即可。
CloseAccount函数,回路复杂度很低,调入函数很高,代码行数很少,只需减少调入函数的个数。
updatepassword函数,是相对危险的,测试需要重点测试的,回路复杂度、调入函数、代码行数,都比较高。
代码复杂度是识别应用程序中可能存在缺陷的一种基本度量,对于识别代码维护性的问题也具有同等价值,复杂度是容易误用的度量,所以重要的是如何聪明 的使用这个度量,并且监视度量的变化,以保证该度量所指示的情况正是你所期望的它能发现的。高度的复杂度只能告诉你这段代码可能会有很多缺陷,但仍需要额 外的调查来证实这个结果。
版权声明:本文出自 shadowwalker 的51Testing软件测试博客:http://www.51testing.com/?622454
原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。
第1章 敏捷团队的自动化测试之旅:第一年
Lisa Crispin
浏览“如何阅读本书”和“案例研究反思”,了解本书章节安排。
Lisa Crispin以其特有的迷人方式描述了当一个敏捷团队决定实施自动化测试时所发生的事情。由于Lisa在敏捷技术方面的专业能力,当看到这支团队在实践中确实非常敏捷时,我们一点儿也没有感到惊讶。这个项目中一件有趣的事情就是:团队(小型团队)里面的每个人都参与了自动化。他们不仅擅长敏捷开发,而且非常敏捷地对其进行了自动化——并且他们成功了。实施敏捷开发并不是这支团队取得成功的唯一要素,其他要素也同等重要,其中包括通过良好的沟通建立稳固的管理关系,以及建立自动化平台来支持创造性的手动测试。另一个关键要素是在团队将过程改进嵌入到整个流程的决策力,包括一年两次的自动化重构的安排。毫无疑问,Lisa和她的团队在他们第一年的时间里所取得的成就是非常显著的。这个项目是为一家美国公司的财务部完成的,特征见表1-1。
表1-1 案例研究特征
(续)
1.1 本案例研究的背景
我们必须面对这样的事实:对于从未进行过自动化测试的人来说,自动化测试是具有一定难度的。本故事告诉我们,面对无任何自动化的测试和有着糟糕设计的遗留系统,这支团队通过一年多的努力,将所有的回归测试都实现了自动化。在接下来的几年时间里,我也与数十个其他面临同样困境并找到类似解决方案的团队进行了交谈。看看我们所遇到的这些困难是否与你所遇到的相似,并考虑用类似的方法进行尝试。
1.1.1 问题
从这里开始着手:每两周我们都需要把新的功能添加到产品中,但是代码bug成灾并且也没有自动化测试,更严重的是,产品中有大量随时会导致系统中断的bug。我们如何摆脱这种情况呢?
1.1.2 目标
我们决心尽自己所能编写出最高质量的代码,但是从哪里开始呢?作为一支自组织的敏捷开发团队,让我们感到欣慰的是整个团队的紧密协作。那是在2003年,我们中有些人在别的团队中曾有过良好的自动化测试经验,他们相信总是会有办法的。我们发现,一个安全的自动化回归测试网络可以让我们更快速地工作。如果我们知道是由于某段特殊的代码而引入的非预期的操作,那么我们能够立即稳定我们的代码库。通过充分的测试覆盖来不断进行集成,使我们每天都有一个稳定的构建过程。而在迭代的后期,现在很难得到稳定的集成,所以这个想法虽然并非那么容易实现,但听起来很不错!
接下来,看看到底是什么帮助我们创建了一个成功的策略来实现自动化回归测试套件。
1.2 整个团队的承诺
我们是由多个程序员、一个测试人员、一个数据库管理人员、一个系统管理人员和一个敏捷教练所组成的团队。公司的业务专家可以随时协助我们。我们整个团队致力于在每次发布之前运行我们的手动回归测试脚本。因为我们每两周发布一次,这意味着两周的迭代周期中我们要花1 ~ 2天的时间进行手动测试。在最终用户使用之前,我们没有足够的时间来进行探索性测试,虽然这种测试可能会帮助我们找到严重的缺陷。
【真知灼见】
敏捷开发团队中的每个人都进行手动测试,所以他们更能体会到自动化测试的好处。
我们都很关心质量,并且我们都致力于保证所有的测试活动都是有计划的且在每一次迭代中执行。这是一个好的开始!
1.3 建立自动化策略
我们需要在不破坏现有功能的前提下发布产品的新功能特性。而且,需要尽快知道一个新的代码变动是否会引起回归测试的失败。手动回归测试在每两周的迭代后期才能给予我们反馈,以至于没有时间进行充分的回归测试。
我们中一些人曾经在其他敏捷团队中进行过测试驱动开发(Test-Driven Development, TDD)。我们发现TDD能帮助创建出设计良好的、健壮的代码。
我们现有的回归测试是手动操作的,整个团队通过使用团队Wiki上所记录的脚本进行手动测试。在每两周的迭代周期中,这就花费了整个团队的20%的时间。这些测试仅仅为程序最核心的部分提供了最小程度的覆盖。产品中报告的缺陷表明,回归测试的失败仍有可能发生。在每次迭代周期中,我们至少要花20%的时间修复这些产品缺陷,从而限制了我们能开发的新功能的数量。
自动化的回归测试会带来更高、更准确的覆盖率,这需要投入大量的时间、金钱和精力,我们可能需要硬件和软件来建立构建过程、持续运行测试所需要的环境,我们也需要使测试实现自动化的架构和驱动。然而,我们可以计算出这一自动化将会节省我们40%的时间,利用这些时间可以进行更多有价值的活动,比如进行新的开发,所以,其收益远大于成本。
继续进行手动回归测试必将会失败,我们需要一个明确的决策来进行自动化。因为我们都在进行手动回归测试,每个人都感受到了没有自动化测试的痛苦。所以,我们有了解决这一问题的动力。首先,我们需要一个可测试的架构……
1.3.1 一个可测试的架构
TDD这条路是要走的,但是当前的代码在业务逻辑、数据库访问和UI等处相结合时,情况比较复杂,自动化单元测试也就变得很难了。往往很难隔离任何一个组件单独进行测试。
这样看来,似乎找到一种把软件进行分层的新架构是非常明智的。我们开发出了这一新架构的所有新功能。
【小窍门】
不要尝试解决老问题,进行新的开发来开展更好的实践。
如果进行自动化测试的成本比其收益高,那么进行自动化测试就没什么意义。我们研究图1-1所示的自动化测试金字塔(这一金字塔是由我们当时的经理Mike Cohn提出的)。单元级别的测试一般ROI最高。程序员可以很快写出它们并运行,而且测试可以根据需要进行更新。因此可以将单元级别的测试作为自动化回归测试的坚实基础。
我们的业务逻辑相当复杂,而且新架构将这一逻辑从数据库和用户接口层中分离出来,这样就可以通过设置内存中数据并在其上运行产品代码来进行测试。这是金字塔的中间层———比底层运行的测试要少但是依然很重要。
我们还需要测试UI,但是通过UI进行的自动化测试本身就非常脆弱、维护费用高且运行缓慢。因为最终想使UI测试所占的比例尽量最小,所以它就处在金字塔的最顶端。尽管如此,与其他团队一样,我们从图形用户界面(GUI)冒烟测试(smoke test)开始,来获取一些代码防护。所以,从这个角度,我们的金字塔是上下颠倒的,但是没关系———最终我们会将它翻转过来。(我们最终花了4年时间才获得为之努力的三角形形状!)
图1-1 自动化测试金字塔
【小窍门】
解决问题的最直接途径未必是最佳途径。
我们现在明白了我们需要做什么,所以我们开始着手实施那个能给我们带来最大实惠的任务。
1.3.2 建立构建过程
我们只有4位程序员,但是他们会经常检查源代码控制系统中的变化。我们需要确保他们没有不小心修改别人的内容或者破坏现有的任何功能。同时,我(测试人员)有时候不得不等待好几天,部署在测试环境中的代码才能完成一次构建。一个自动化的构建过程(build process)将保证在每次检入后的几分钟内,最新代码的可部署版本就是可用的。
管理层向我们强调了质量是我们的第一目标,他们乐意宽限一些时间来让我们搭建一个好的基础结构。
【真知灼见】
好的管理者会准许团队花时间去开发自动化的基础结构。
我们停下正在做的事情,使用CruiseControl和一个新的Linux服务器建立了一个持续集成(Continuous Integration, CI)过程。因为我们还没有可以运行的任何测试实例,所以只是简单编译代码和构建可部署的二进制文件。我可以看到每次构建的检入文件并能够选择想部署到产品中的二进制文件。这很有用,但是还不够。
1.3.3 获取测试的基准:GUI冒烟测试
程序员正在学习如何使单元测试自动化并编写测试先行(test-first)的代码,但是要真正实施测试驱动开发需要花好几个月的时间。针对遗留代码,使用GUI冒烟测试可能是一个快速获得自动化测试覆盖的途径。但是使用什么工具最好呢?
我们有购买一个付费工具的预算资金,但是团队里的程序员是Java程序员,他们万不得已是不愿意使用另一种脚本语言来进行测试的。捕获/回放并不适合我们,因为我们需要可维护的、稳定的测试脚本。我们选中了 Cannoo WebTest——一个可以让我们修改XML文件中的测试用例,并使用Ant运行它们的开源框架。而且它很容易与我们的构建过程集成。
【真知灼见】
昂贵的商业工具不一定是最好的选择。
我让业务专家把需要用冒烟测试来保护的系统核心区域按优先等级进行划分。每个冲刺时间段里,都安排了时间让我用WebTest工具自动运行测试脚本。我们首先追求的是“快赢”,针对系统中每个用户角色的基本功能实现自动测试。
首先, CI构建过程只运行了少量单元测试和一些覆盖系统高得分点的GUI冒烟测试。随着在这两个级别引入更多测试,我们将GUI测试移到单独的构建过程中并只在晚上运行,这样,我们可以更快得到的反馈信息。
与此同时,我们将单元测试和每个新用户故事(user story)的GUI冒烟测试都放在迭代中完成。新的功能将会在自动化的单元测试和GUI测试中被覆盖。一旦程序员能够熟练使用TDD,我们就将进入金字塔的中间层。
1.3.4 在单元级别驱动开发
我们的程序员中只有一人曾经实施过TDD,但每个人都支持这种理念。我们根据别人的介绍,找到了我们能找到的最好的顾问来帮助我们学习如何实施TDD,很多时候我们都是自带午餐以便挤出时间来进行TDD试验。但是很遗憾的是:它很难学!我们团队需要时间来掌握它。
我们的管理层知道这个道理:目标是写出优美的代码,优美到可以拿回家给妈妈,当做冰箱贴。我们的公司——一个刚刚成立3年的商业公司,因为网络应用问题和无能力及时发布新功能而面临倒闭。我们的商业合作伙伴也准备放弃我们了,但自从我们实施敏捷开发(Scrum)以后,他们看到了我们为提高稳定性和响应能力所做的努力。我们的执行者致力于具有长远收益的投资。
【经验教训】
一个迫在眉睫的灾难可能是巨大进步的推动力。
我们知道这些单元级别的测试具有最好ROI。我们也理解TDD事实上是代码设计,而不是测试。考虑“代码是用来做什么的”可以帮助程序员写出正确的代码,而快速实施测试,才能及时地提供重要的反馈信息。
单元级别测试的最大好处就是能够提供最快速的反馈。在研究一些好的实践之后,我们决定将代码构建过程的时间控制在10分钟内。这需要单元测试的随机重构。早些时候,单元测试需要访问数据库,之后,程序员掌握了如何构造、模拟它,以及消除它的影响,使测试快速运行又能提供正确的测试覆盖。
1.4 利用验收测试驱动开发,使用FitNesse测试GUI
现在已经是我们自动化之旅的第8个月了,程序员已经建立了一个自动化单元测试的实用库。对于应用程序的核心区域我们已经进行了冒烟测试,覆盖微量代码的大约100个JUnit测试已经完成了。但是中间层还什么都没有,TDD此时变成了一个空壳。现在我们开始对自动化测试金字塔的中间层进行填充。
1.4.1 内存内测试
我们的金融理财产品有许多复杂的算法,这可以通过在内存中提供输入来进行测试。与单元测试相比,这种测试的级别要高很多,但我们还是不想通过GUI来进行测试,因为其速度慢、成本高。我们发现可以很迅速、方便地编写FitNesse夹具(fixture)来自动进行这类测试。在客户测试层次,我们开始用FitNesse测试来驱动新的用户故事的开发。这些测试使用夹具在内存中构建测试输入,并把这些输入发送到应用层代码,就如同产品在实际使用时进行的输入一样。然后夹具会返回代码的实际输出,并把它与FitNesse表中的期望结果进行对比。
测试人员和客户填写FitNesse测试用例,之后程序员用夹具来自动运行它们。这意味着我们需要交流!提高团队的交流能力是使用这种工具进行自动化测试的最大好处之一。我们测试人员与产品所有者和其他利益相关者坐在一起分析每个用户故事预期的和非预期的行为。我们将这些用户故事转化为FitNesse测试用例表,并与客户核对以保证当测试用例通过测试的时候能够满足客户的要求。我们与开发人员核对测试,以保证我们清楚需求并保证测试设计与代码设计兼容。开发人员编写夹具来自动运行测试。这一过程在单个用户故事的很多小的迭代中不断重复,直到开发和测试都完成。 1.4.2 使用数据库的测试
我们的应用是数据密集型应用,我们想自动运行更加端到端(end-to-end)的测试。我们也可以使用FitNesse在数据库中构建测试数据,并使用遗留代码在它上面运行,以此来测试遗留代码。
这种类型的测试脚本编写和维护都更加昂贵。作为FitNesse的初学者,我们犯了一些错误,如,不知道将测试组件模块化,而这可以通过FitNesse中现有的部件来完成。我们彻底违反了代码设计中的“不要重复自我”准则。例如,在几十个不同的测试页面中,有包含同样员工信息的表,如果又有一列新数据需要添加到员工信息表中,那么在每个测试页面中都要加进去,这很糟糕。等到我们知道如何正确去做的时候,又遇到了一个更难以处理的麻烦。
【经验教训】
在前期进行工具使用的培训,之后就可以避免因工具使用不熟练而造成的巨大的时间浪费。
我们将每个能想到的测试用例都进行了自动化,包括发生几率低、影响小的边缘测试用例,并把它们都放在自动回归测试套件(test suite)里面。上述过程花费的时间并不长,但是接下来,这个测试套件要花费大量时间和主机功率(machine power)来运行,维护成本也随之提高了。所以,我们要学会慎重选择测试用例,确保它们能提供充分的测试覆盖,并只将这些测试用例放在回归测试套件中。
【小窍门】
精化回归测试套件可以在保证收益的同时降低维护费用。
正如在产品应用代码中所做的那样,我们现在不断地重复访问和重构FitNesse测试用例,以保证我们所需要的测试覆盖,同时不会过多地延长反馈周期或者花费太多时间维护测试。
【真知灼见】
经常检查自动化测试用例以保证它们是有效的。
1.4.3 使用FitNesse测试的好处
我们的FitNesse测试提供了比GUI测试套件更快的反馈,尽管比JUnit测试要慢很多。FitNesse测试套件需要60 ~ 90分钟来运行,而JUnit测试的运行时间仅仅只需要不到8分钟。像GUI测试脚本那样,我们将FitNesse测试集成到构建过程中,并且在其中运行。一开始,我们只在晚上运行这种“完全构建”(full build),但这无法提供及时的反馈,并且如果测试失败的话,我们只有在第二个晚上才能知道问题是否修复了。我们投入了更多的硬件,这样我们就可以“连续地”运行单元级别上的所有测试的完全构建。如同运行单元级别测试的构建一样,这是为了使源代码控制每接收到一次检入就能运行。大概需要90分钟运行一次,所以经常同时测试几个新的检入。
(未完待续...)
相关链接:
自动化测试最佳实践 连载一
自动化测试最佳实践 连载二
用好WinRunner要从两方面去做:
一是熟悉WinRunner,尤其是要熟悉其 TSL 脚本语言,这一点其实不难,完全可以做到在拿到程序之前就写好测试脚本的。
二就是要有有好的测试用例,一个好的测试用例才是一个成功的测试用例。
那么如何实现在拿到产品之前,就能写好一定的测试脚本呢???
(1)事先编写TSL测试脚本,不需要知道GUI对象的分布,只需要知道有些什么GUI对象,需要对其进行什么操作等。
(2)通过录制+少量修改的方式得到的测试脚本只能做比较少的测试工作,通过录制得到脚本是建立在操作的流程正确的基础上的,如果本来就错了的话,录制的脚本即使回放正确,程序仍然是错误的。录制+ 简单的修改不叫自动化测试。
(3)回归测试是自动化功能测试工具的强项,但并不表示自动化测试工具主要就是做这些地,第五代自动化测试工具已经具备了事务处理的能力,W inRunner 7.0已经支持事务处理,这是录制脚本无法达到的,必须人工编写。
(4)我们有很多的测试人员抱怨测试收入低、不受重视等等,为什么会这样?我想,除了大环境的原因外,我们也应该从自身来找找原因。我能够发现多少错误?我对业务知识了解有多少?我对测试领域的了解有多深?我能够为公司的质量管理提供多少改进依据?我的测试流程做得有多好?为什么欧美的测试人员受到重视,收入高?除了大环境以外,众多的专家和对质量保证的贡献也是原因之一吧!
(5)如果希望工作能够非常轻松的话,绝对不要干软件测试这一行!不管是手工测试还是自动化测试,都不是轻松的事,维护测试用例库是应该要做的事情。自动化测试的重点就是前期的测试设计和后期的结果分析,而且前期设计的时间可能是手工测试设计的数倍甚至上十倍,想轻松是不可能的!
(6)有几种情况不要考虑做自动化测试:
a:易用性测试
b:一次性测试
c:立即测试
d:无预期结果的测试
我们根据多年经验,总结软件全程质量保障的技术体现,希望能给大家带来帮助,也欢迎大家一起进行探讨,学习,不足之处,请指正。
软件项目全程质量控制的框架,主要包括以下几个模块:
● 质量需求及质量需求分析:实施全程质量控制单位案的基础是质量需求的采集和分析。
● 建立质量保证机制:全程质量控制单位案是一个多方协同的项目实施过程,科学有效的质量保证机制是确保方案顺利实施的基本保障。
● 基于过程的质量保证模块:主要包括产品审计、过程检查、计划跟踪和风险评估;
● 基于过程的质量控制模块:主要包括多级别软件测试(单元测试、集成测试、系统测试、验收测试及其他性能测试等专题测试)、阶段评审和代码检查及评审。
● 技术支撑:主要包括过程管理技术(软件过程管理、项目管理体系应用和技术评审的实施)、软件测试技术(模型检测技术、自动用例技术和性能测试技术等);
● 关键过程域:全程质量控制主要包括软件需求过程质量控制、软件设计过程质量控制、软件编码过程质量控制和软件交付过程质量控制等4个关键过程域;
● 全程缺陷管理:伴随全程质量控制的实施,软件项目大量缺陷会被发现,如何对这些缺陷进行有效管理是确保全程质量控制实施效果的关键。因此,全程缺陷管理是全程质量控制的一个必不可少的组成部分。全程缺陷管理依据缺陷生命周期进行缺陷的跟踪和控制,采用自动化管理工具实现缺陷的发现记录、跟踪消除,确保每一个缺陷得到合理的处置。
● 质量评估:通过全程质量控制的实施,可以积累、采集软件项目的多个层面的相关开发数据,对这些数据进行科学分析,我们可以对整个项目的过程、成果进行有效评估,从而为各项决策提供有力有理的依据。
一、ROUTE1:质量保证
产品审计:对项目各阶段产生的主要工作产品(如项目计划、软件需求规格说明书、设计文档、重要的源代码、测试文档、用户使用文档等)进行审计,检验工作产品是否符合预定需求、在格式和内容上是否符合适当的标准,以此来保证项目实施过程中产生的工作产品的质量。评审标准将采取产品规范与实际需要相结合的方式,提高工作产品的实效性。
● 过程检查:软件质量保证活动中的核心工作,其主要目的在于对软件项目开发过程中重要工程技术活动的工作过程按照预先确定的工作规范和检查准则进行检查,发现其中的不符合项,及时通知项目相关方并采取纠正措施,以确保项目过程的质量。
● 计划跟踪:在整个项目的开发建设过程中,以《项目计划》为主线和依据对项目进展情况进行跟踪与检查。
● 风险评估:目的在于发现项目进展过程中存在的风险问题并提早做出应对措施,以便降低或减小风险问题给项目造成的影响或损失。风险评估主要根据项目相关方的需要,在项目进展过程的关键阶段(重大里程碑)进行。
二、ROUTE2:质量控制
● 技术评审(Technical Review,TR)的目的是尽早地发现工作成果中的缺陷,并帮助开发人员及时消除缺陷,从而有效地提高产品的质量。
● 代码检审包括代码检查和代码审查两部分内容,主要检查代码的编制和设计的一致性,代码对编码规范及其他标准的依从性,代码的可读性,代码逻辑表达的正确性,代码结构的合理性等。通过代码检查及时发现违背编码规范的问题,代码中不安全、边界溢出、死代码等问题。
● 文档检查:对于系统建设期间产生的各类文档应该进行认真的检查,必要时实施文档测试。
● 单元测试是对软件设计的最小单位(模块)进行正确性检验的质量控制工作,测试并发现模块在语法、格式和逻辑上的错误。用单元测试用例测试程序,将实际结果与期望值进行比较,以验证模块设计的正确性。
● 集成测试是在代码审计的基础上,检验在将所有的软件单元按照设计说明书的要求组装成模块、子系统或系统的过程中,各部分工作是否达到或实现相应技术指标及要求。
● 系统测试是将通过集成测试的软件,作为整个基于计算机系统的一个元素,与计算机硬件、外设、某些支持软件、数据等其他系统元素结合在一起,在实际或模拟运行环境中,对系统进行一系列测试,以发现软件与系统定义不符合或与之矛盾的地方。
● 专题测试,是根据项目委托方对软件的特定质量需求,进行有针对性的强化测试。
● 验收测试主要依据软件开发商和用1户之间的合同、软件需求说明书以及相关行业标准、国家标准、法律法规等对软件的适合性、准确性、互操作性、保密安全性、成熟性、容错性、易恢复性、易理解性、易学性、易操作性、吸引性、时间特性、资源利用性、易分析性、易改变性、稳定性、易测试性、适应性、易安装性、共存性、易替换性和依从性方面进行严格的测试。
版权声明:本文出自山东省软件评测中心 张凯丽,51Testing软件测试网原创出品,未经明确的书面许可,任何人或单位不得对本文进行复制、转载或镜像,否则将追究法律责任。
http://www.51testing.com
前言
本文主要是阐述个人的web手工黑盒测试的工作经验
测试目的
测试并不仅仅是为了找出错误,通过分析错误产生的原因和错误的分布特征,可以帮助项目管理者(开发人员)发现当前所采用的软件过程的缺陷,以便改进;从而提高软件的质量,更体现了测试的重要性。
工作经历
1、工作环境介绍
09年3月刚入职,也是项目初建阶段,项目组6个人在一个小房间,5台台式机1个人用笔记本(领导);2张桌子比较挤,工作的地方是比较简陋;刚开始熟悉需求,然后是和同事一起部署项目,不过就是看看表结构,学习下怎么把数据入库Oralce数据库,后台Oracle存储过程开发,在前台配置业务指标配置展现,给用户做个什么小需求等等,都是琐碎的事。不过项目初建比较累,加班较多,事情也多,大概到8、9月份才开始正常上下班。 10年过完年大概3月份左右吧,二期的项目要测试(公司给我们项目组划了一片办公地方,挺好的),项目组人手不够,老大让我转测试,问我同不同意,我想了想自己的工作内容比较杂,专注一件事情也是好事,就同意了!开始真正的测试工作。
然后老大从别个项目组调了一个有测试经理级别的人,过来协助测试,我跟着他学习了一点东西。比如测试用例的撰写,用户验收UAT用例和测试报告的输出。记得他说过做测试要细心,提出的bug要跟踪,注意页面的美观性,按钮、字体大小、字体颜色,风格要保存一致等,虽然他教的少,不过还是挺感谢滴!
二期项目上线之后,测试工作告一段落,我又恢复了以前的工作,没有了测试工作就做业务需求开发写写Oracle存储过程,前台配置展现,维护下测试环境和线上的环境等等。
做了测试之后感觉自己挺喜欢这行滴,因为工作的事情比较杂想学不到什么东西,个人想专注测试,2011年动摇了要离职的念头,不过老大找我谈了好几次话,也主动给我加了工资,就留下了,遇到这种老大,挺不容易的,对我们组员很好。
11年项目三期测试;输出测试策略,按照计划的时候输出相应的文档,比如测试用例的输出,然后全员参加用例评审(开发、测试和PM),会上提出用例的不足或者、与需求不符或者不完善的地方;修改了之后发送PM,通过后执行测试。测试的时候每天晚上邮件反馈当天的工作进度,采用迭代测试的形式测试,测试环境我自己维护,一轮测试完成后,开发把bug修复完成,在提供一个发布包,然后验证,没有新bug产生后,就输出一份系统测试报告和缺陷报告(针对开发人员)。如果客户要求做压力测试,需要输出一个压力测试方案(包括场景、测试模块、测试环境等),当然方案也要评审,评审通过后开始LoadRunner压力测试,测试完成后输出压力测试报告。然后是一个用户验收UAT测试用例的输出。最后上线完成。并输出一个上线总结文件!
在工作期间带了3个新同事,Ta们3个都不同,也许是刚开始接触测试,慢慢的成长!有一个女同事很…..我给她定了个学习系统和业务的计划,人家自己不做反而在那里看开发的代码,问她的时候她也总是没问题,偷懒很严重,如果她不是女生就和老大说不用她了……
介绍下以前公司的测试流程:
……………………
查看全文请点击下载:http://www.51testing.com/html/76/n-844176.html
2.4 数据验证
1)前后台数据一致 : 前台正确录入信息保存后,后台数据库相对应的表正常记录(与前台输入一致)
比如:注册一个用户信息提交成功后,用户表users中是否正常保存了当前的录入信息。
2)存储过程验证:oracle F8编译通过,F8执行后 对应的数据表正常录入数据,无锁表现象(当目标表B表从另外一长表A表取值,当A表数据过大时要借助临时表,避免死锁、耗费资源的现象)
2.5 根据开发习惯找错误
1)同一个开发人员开发的模块,在不同的模块犯了错误,其他的模块也有类似的错误
比如A开发人员 主要负责用户、权限模块,在测试用户模块时发现用户名可以重复,现象用户名重复: 注册了两个相同的帐号,但是用户状态不同,一个是不可用状态,一个是可用状态,但是登录的时候两个都不能登录,提示“帐号不可用”。然后再去验证权限模块,角色名称也可以重复,看似小问题,但对于用户来说可能就是大问题了,因为正常状态的用户不能登录。所以开发人员的习惯也是不能忽视的!
2.6 LR压力测试
选择好录制协议,录制脚本,根据需要添加 事物和集合点 ,使用参数化,设置runtime-setting ,在场景执行的时候 注意观察主机CPU和内存使用率。
个人观点
1)立项前的需求分析很重要,与开发人员的沟通也很重要;对需求理解程度越深,对开发的思想理解越透彻,撰写的测试用例就越全面,漏测的几率也会减少。
2)关注用户的需求,注重细节,尽可能找出系统中隐藏的缺陷。
3)总结测试过程中发现的问题,做好漏测记录,避免相同的错误发生。
查看全文请点击下载:http://www.51testing.com/html/76/n-844176.html
本文收录于《51测试天地》电子杂志第二十九期。
版权声明:本文出自51Testing软件测试网电子杂志——《51测试天地》第二十九期。51Testing软件测试网及相关内容提供者拥有51testing.com内容的全部版权,未经明确的书面许可,任何人或单位不得对本网站内容复制、转载或进行镜像,否则将追究法律责任。
TQA是一项系统的复杂过程,要做好全程质量控制需要从项目管理的高度去统筹进行。为了方便叙述,我们以服务受理进程和信息系统建设的瀑布模型为基线进行阐述,希望能给大家带来帮助(如图1所示)。
图1 TQA服务流程示意
一、TQA市场支持
TQA服务咨询由我中心专门的TQA服务咨询师提供服务,通过深入的客户沟通,依据客户需求量身定制个性化TQA技术方案,并在此基础上进一步沟通,直至获得客户认可。
在TQA技术服务方案确认后,双方即可签署TQA服务合同,以商业合同的形式确认双方的责权利关系和内容。
通常在TQA合同签署3个工作日内,中心分配测试任务,并指定项目负责人和职能工程师(如项目管理师、系统分析师、资深SQA、资深开发工程师和资深测试工程师等),完成TQA服务项目组的组建。
二、TQA服务实施
TQA服务一般是一个长程的伴随式服务,不同阶段对应不同的目标和任务,视工作需要安排不同角色的工程师完成相关的服务。下面分述如下:
1、系统规划咨询
系统规划咨询由系统分析师负责实施。通过企业现状调查与分析、系统需求与用户需求分析进行信息规划,协助客户做出投资概算、收益与价值估算、技术可行性分析、风险分析、工作计划等内容。
2、应用系统方案评估及比对测试
应用系统方案评估及比对测试一般由资深测试工程师实施。一般依据整理的初步的系统需求和功能需求以及系统规划,搜集、整理可能的解决方案,并对整理后的解决方案进行评比、选择。
对于系统的选择要取得可信的评比指标需要进行比对测试。所谓比对测试是在各个被选系统中制定一份可度量的评比基准,在此基础上对系统进行功能、性能的采样,采集提取各项系统指标,形成系统比对报告。
3、应用系统成本估算
应用系统成本估算由系统分析师负责实施。在项目立项、系统招标、项目决算以及软件生命周期的各个阶段对应用软件系统的规模和工作量、成本进行估算,并提供应用系统的规模和成本评估报告。
三、信息系统建设与开发
信息系统建设与开发是一次信息系统建设的核心内容,这个过程的质量控制的优劣将直接决定信息系统建设的成败。这个阶段的质量控制策略我们从需求工程与阶段评审、设计与开发技术咨询与技术阶段评审、软件测试咨询与过程测试、技术评审与质量保证、项目管理咨询和配置管理咨询等6个方面进行阐述。
信息系统建设与开发阶段主要参与者包括项目管理师、系统分析师、资深SQA、资深开发工程师和资深测试工程师等。
1、需求工程咨询与阶段评审
软件功能需求是在系统需求、用户需求基础上的细化,是软件实现的功能描述和规范。如果其描述不清或不明确,将使后面的设计开发工作失去依据,也缺少软件评价的标准和依据。因此需求工程需要采用科学适用的过程,如需求采集、需求分析、需求管理等,对需求的变更进行严格的控制。
需求工程实施后一般会产生《需求规格说明书》,这是需求工程的核心文档,对这种具有里程碑意义的产出物我们会组织并邀请客户代表、业务专家共同进行专家评审,已确认需求的有效性。
2、设计与开发技术咨询与技术评审
设计评审的内容一般包括:
评审模块是否覆盖了需求以及模块划分是否合理;
界面设计是否符合业务以及是否易用;
数据库的设计是否覆盖业务内容且易于扩展;
是否提供数据的完整性保证措施;
系统性能是否能满足需求中定义;
设计是否考虑了业务高峰的处理以及瓶颈分析解决措施;
是否考虑信息系统的故障对策,系统的故障对策是否完备;
系统的权限及安全保证措施;
3、软件测试咨询与过程测试
图2 软件测试过程模型
通过软件测试咨询协助项目组建立起适合的软件测试过程模型(如图2所示),规范测试分析、测试设计、测试执行和缺陷处理的测试进程,并约定各个阶段的工作规范(我中心于2008年主持定制了软件测试过程的国家标准GB/T 15532 《软件测试规范》)。
测试组织按照既定规范开展测试工作,对软件开发与实施过程中的各个阶段性的产品进行测试和确认。根据软件开发合同或计划,针对各个阶段的产品进行严格的测试,包括单元测试、集成测试、系统测试。在整个项目完成后,提供系统级的验收测试,通过严格的测试,来消除系统潜在的问题。
4、技术评审与质量保证
技术人员将参与软件开发与实施的全过程,对阶段性的成果、产品进行评审、测试、验证和确认。从软件开发过程中间来保证软件的质量,为软件的后期使用、维护等工作奠定良好的基础。
5、项目管理咨询
结合CMM/CMMI的有关规范及PMP、IPMP的项目管理知识框架对项目进行管控咨询。
6、配置管理咨询
配置管理活动被用来设计标识配置项、建立基线、控制版本和变更、保证变更被适当地实施,使开发人员、测试人员、项目管理者、质量保证人员等能方便地通过软件配置管理获得有用的信息。在现代信息系统建设项目实施过程中,配置管理已成为重要的一环。
通过配置管理咨询,将我们的成功经验传导给项目组,通过配置管理识别和工具选型培训等建立起规范的配置管理体系。
版权声明:本文出自山东省软件评测中心 张凯丽,51Testing软件测试网原创出品,未经明确的书面许可,任何人或单位不得对本文进行复制、转载或镜像,否则将追究法律责任。
http://www.51testing.com
相关链接:
软件全程质量保障TQA概述
0.2 技术因素
在我们看来,最重要的技术因素是测试件架构以及多个层次上的抽象。测试件(testware)是所有创建的用于测试的事物,包括脚本、数据、文档、文件、环境信息等。测试件架构就是这些事物是如何组织的,以及它们彼此之间是如何依赖的——例如,高层次的脚本使用了低层次的脚本用来与被测软件进行交互。
0.2.1 抽象、抽象、再抽象:测试件架构
在最初得到了一个测试执行工具后,通常会期望测试人员开始使用这个工具。当这名测试人员不是开发人员时会发生什么?现在突然需要他们成为程序员,去学习工具的脚本语言。如果他们没有编程背景,他们就不知道如何构建坚固的(自动化)代码,于是当对被测软件进行改动,同时这项改动也需要在自动化代码中进行时,维护他所写的代码可能就要付出很大的代价。通常,公司放弃一个测试执行工具是因为测试脚本维护费用很高!
对于成功的自动化测试执行来说,(至少)有两个主要的抽象层次:将工具和工具特定细节与结构化的测试件分离开来;将测试(即测试人员所从事的工作)与结构化的测试件分离开来。这些内容会在0.2.1.1节和0.2.1.2节中进行更详细的描述。
自动化测试人员负责实现这些层次的抽象;如果这些层次实现得不好,那么自动化维护起来就会很昂贵而且很难使用。图0-1展示的是我们认为好的测试件架构,其中还包含了为了达到这些层次的抽象自动化测试人员所扮演的角色。
图0-1 抽象的层次
0.2.1.1 将测试执行工具与测试件分离开来
一个好的测试件架构能够让测试自动化发挥真正的力量。自动化测试架构师负责设计测试件的架构:脚本、数据文件和实用工具等,这些都是自动化测试所需要的。其他的自动化测试人员在之后必须对其进行补充和完善。这里需要好的编程实践:良好的结构,遵守一些合理标准的模块化代码,这些标准鼓励代码的简易性和复用性。当软件的一些方面有所改变时,也许测试件需要进行改动,但是一个好的架构会让这些变动最小。
可以通过框架来实现测试件架构,框架可能是测试执行工具的一部分,也可能和测试执行工具分开;或者也可以通过分开的测试控制程序或者测试解释器来实现测试件架构。
有时人们在用户界面(UI)对象(如按钮和编辑框)周围使用包装器(wrapper)。包装器是个好的开始而且对于好的自动化测式来说也很有必要(但是并不足够)。为GUI对象的名字使用对象映射(object map)也是另一个用来实现合适层次抽象的例子。
另外一个重要的方面是较高级的测试件不应该直接与任何特定的工具或者工具语言绑定在一起。当然,在工具接口上,脚本必须是以选择的工具语言所编写,但是这仅仅是对于最底层而言;结构化的脚本应该以与工具无关的方式编写。你也许会认为,当前使用的工具很完美,为什么还要这么做?但是随着时间的推移工具会发生变化,应用程序也已意料之外的方式发生改变,系统可能要迁移到其他环境或者平台上,于是3年前看起来完美的工具现在看起来并不那么合适。如果测试件和工具紧紧地绑定到一起,那么很有可能你最终会不得不抛弃测试件的很多部分。如果达到了这个层次的抽象,那么就可以保留结构化的测试件的大部分,仅仅需要改动最底层的那些与工具相关的脚本。
这个层次的抽象能让自动化测试有着更长的生命周期。通过使用正确层次的抽象,无论是在应用程序上进行了改动,还是在当前使用的工具发生了变动,依然可以使得自动化测试继续进行下去。在本书提到的所有成功案例研究中都在一定程度上实现了这一层次的抽象。
0.2.1.2 将测试与测试件分离开来
顶层(高层)的抽象是将测试的基本想法(即对什么进行测试)与测试的实现(如何构建自动化测试)分离。测试人员使用对他们有意义的高级(领域相关的)关键字编写测试,以描述他们想要运行的测试。
这一级别的抽象对于那些没有编程技能的测试人员是最重要的。他们的专业技能可能是有关业务或者应用程序的。如果是程序员在开发测试,他们会更加理解这些测试技术是如何实现的,于是这层的抽象对他们来说就没有那么重要;这些对于开发人员最合适的高级关键字可能对那些业务用户来说过于技术性。但是不要忽略这一点,即对于那些编写和维护测试的人员来说,易于使用这些测试是很重要的,而不管他们是不是程序员。
测试人员的工作是测试(见0.1.5节中关于技能的部分)。在关注如何最好地对一些事物进行测试时,不应该考虑过多的实现细节。他们应该能够使用对他们来说有意义的语言来编写测试,而不是使用只对工具有意义的编程语言。他们关于测试的想法与他们的领域有关(即他们测试的应用程序),于是他们的测试也应该与这个领域有关。
例如,一个拥有保险业务知识的测试人员可能会关注保险索赔和续保。一名专注移动设备的测试人员可能会想对网络连接、会议电话等进行测试。对于不同行业部门的人来说,这些高层次关键字可能非常不同,即使一些低层次的关键词可能会相同。这就是为什么我们喜欢这个词目——“领域相关的测试语言”(Domain-Specific Test Language, DSTC),这点Martin Gijsen曾也提到(www.deanalist.nl)。
如果没有任何编程经验的测试人员既能编写也能运行自动化测试,那么自动化测试就能对业务领域有巨大的帮助,而且更有可能广泛地应用,进一步增加ROI。
专门地讨论这个层次抽象的案例研究包含在第2、11、12、14、15、20、21、27和29章。
0.2.2 自动化测试标准
如果人们仅仅关注自己的事情,他们将会以只对他们有意义的方式创建自动化测试和测试件,然而他们创建的东西彼此之间都不相同。如果想要很多人使用自动化测试,那么定义自动化的标准并去遵守这一标准是很重要的。标准的命名约定,存储测试件条目的统一的地方,创建测试的标准方式,归档测试的标准例子,对象、脚本和数据文件的标准名称;每个脚本的标准文件头——需要达成一致并且普遍使用的事情远比你能想到的还要多。之外,使用自动化测试的人越多,遵循标准就越重要,并能带来更大的收益。
对这些自动化测试标准的定义最好在试点项目中开始,在试点项目中可以尝试各种方法并且达成一致。之后必须将大家都同意的标准告知所有将要使用自动化测试的人员——无论使用领域相关的测试语言定义自动化测试的人员,还是开发可复用脚本的人员。我们还建议将一些标准构建到支持测试的工具中,以便更容易地创建更多的脚本,而且还能避免很多人为错误。
然而这些标准也不应该一成不变。随着自动化测试变得成熟,保证标准可以变更以及遵循新的(改进的)方法进行更新是个很好的主意。另外很重要的是,允许标准中出现例外情况,当然,要对这些例外进行验证。随着自动化测试规模变大,将标准进行集中管理并且就其与所有人员进行沟通就变得更加重要。关于标准的讨论参见第6、9、12、15、21和29章。
0.2.3 可复用性、文档和灵活性
虽然有时构建一些“可以丢弃的脚本”(disposable script,参见第19章)是很有用的,然而大部分自动化测试中,单个脚本都有很长的生命周期,并且需要以多种方式复用。如果脚本使用多次,那么就值得花费时间去保证它构建良好,如其中包含良好的错误处理方法,并且保证对其进行过测试和审阅。去找寻那些可以复用的模块,而不是一切都从头编写。
为了使得测试自动化人员能够找到并且使用可复用的模块,这些模块必须有良好的文档。应该可以很容易地访问这些测试件条目的信息,而且这些信息应该以标准的格式呈现出来。例如,在每个脚本头,写出这个脚本的目的、如何使用这个脚本(它期望接收的数据以及返回的数据),以及一些前置条件和后置条件。一些额外的信息也很有用,如在一些可能的错误条件下会发生什么。这些信息应该可以很容易地搜索到。事实上,将这些信息收集起来就可以作为测试件的文档,而且通常可以很直接地将这些信息收集起来,并将其分类到单独的文档或者集中进行管理。
在运行测试时,灵活性是很重要的。例如,如果一个脚本正在处理从电子表格中得到的数据,而且并不需要电子表格中所有项都填满,那么一个更加灵活的脚本应该允许跳过那些没有值的字段,这可使测试人员很容易创建各种不同的测试。
为每个测试标注上该测试属于哪一类也是很有用的,例如,可以标记其为冒烟测试、修复bug的测试或者对函数X进行的测试。使用这种“测试选择器”可以很快地选择在给定时间运行测试的不同子集。
这些主题在第3、5、11、14、21和29章中讨论。
0.2.4 测试结果和报告
评估测试的结果是测试所做的最基本的事情:如果不关心结果是什么,那么它就不是一个测试!然而对自动化测试的结果进行检查并不是一件显而易见的事情,尤其是当检查结果是以屏幕截图的位图存储时(要尽可能避免),对结果进行自动化检查就更加困难。
工具不会告诉你一个测试通过还是失败;它仅仅能告诉你测试结果和你期望的结果是否匹配。如果期望结果就是错误的,那么工具可能很“高兴地”认为所有的测试都通过,即使它正在做错误的事情!
有时,自动化测试可能愚弄你,让你认为它们正在检查一些东西,而实际上没有——通过的测试通常不会有人进行检查,然而这有时会带来严重的问题,正如第15、19和29章所讨论的(第15章称这些测试为“僵尸测试”)。
对于测试结果的报告应该让收到报告的人能够理解这些测试;为此可能需要做一些额外的工作来过滤以及解释原始的测试结果,但是这些工作很值得,因为这能让各类人员对这些测试有着更好的交流。这些主题在第3、5、13和20章中讨论。
有时通过使用部分的预言(oracle),自动化测试仅仅能够检查测试结果的一部分而不是所有。例如,探索性的自动化测试(第28章和第29章)、猴子测试(第24章)和可靠性测试(第13章),可能会检查系统是否还在运行而不是检查系统的结果是否正确。当能够自动地生成大量测试时,仅仅检查系统是否能存活下来也是很有价值的。
更多的关于测试结果和报告的讨论见第3、5、10、13、14、17、19、20和29章。
0.2.5 对测试进行测试:审阅、静态分析、对测试件进行测试
自动化代码(包括脚本)也是软件,正如所有的软件一样,它们也会有bug,并且必须对其进行测试。对自动化测试件进行审阅,甚至检查,也能帮助找到在测试件本身中的bug和问题,并且保证自动化测试与它的目标是一致的。这还能够有助于团队内沟通知识,让团队人员更好地了解测试件的细节。不需要审阅所有的事情——选择一个具有代表性的测试方案,并且与利益相关者对其进行审阅是一种很有效的方式,有助于确定正在进行的工作是否正常。
正如第15章所述,另一个来自软件开发的技术,是对测试脚本的静态分析。静态分析工具能够发现代码(包括自动化代码)中的bug,这些bug在评审过程中以及测试中可能发现不到。虽然静态分析不能找到所有的bug,但其发现bug的速度很快而且成本很低。
这些主题在第1、5、7、10、15、17、21和29章中讨论。
0.2.6 对哪些测试进行自动化
第2章和第12章证明了对那些没有价值的测试进行自动化是没有意义的——首先,保证将进行自动化的测试是值得自动化的;其次仅仅自动化那些重复使用的测试。
首先应该对哪些测试进行自动化?那些对测试人员来说执行起来很无聊的、那些普通的并且重复的、或者那些非常复杂的测试,都是开始自动化的最佳候选测试,但是也要根据当前最需要解决的问题来进行选择,总的来说,要对那些能够为测试带来价值的测试进行自动化。
第25章给出了一个用于决定是否对某个测试进行自动化的检查列表。知道不要对什么进行自动化以及应该对什么进行自动化是很重要的。过多地进行自动化也会影响自动化的结果,因为这可能使你难于获得一些“快速的回报”(quick win),虽然这些回报能更有效地证明自动化的价值。
这些主题在第2、8、19、20、22、25和29章中讨论。
0.2.7 自动化测试不仅仅是执行测试
大多数人认为测试执行工具仅仅是用来执行测试的工具。然而,要达到好的自动化,仅仅对一项活动有着工具支持是远远不够的。书中很多案例研究都讲到,创造性地思考通过工具能支持自动化过程中的哪些活动——不论是通过使用传统商业工具还有内部实用工具、脚本、宏等,都能给项目带来惊人的收益。第19章主要关注这个主题,除此之外,第3、4、5、14、21、22、24、25和29章还包含了一些有用的小窍门和主意。
在自动化测试过程中经常被忽略的一个领域就是,将手动测试引入到自动化过程中是很有用的。第21章讲述的例子很有趣,它在改进自动化的同时也加入了对手动测试的支持,在第1、8、12和19章也讨论了这个主题。
另一个常被忽略的领域是对于预处理和后续处理任务的自动化。如果不能对这些任务进行自动化,那么自动化测试就不得不加入手动过程——这在很大程度上违背了自动化的目标!关于这个主题的更多讨论请参见第2、10、11、18和22章。
0.2.8 失败分析
当测试失败时分析原因可能会花费大量时间,对自动化测试失败的分析所花的时间要多于手动测试所花的时间。例如,手动测试时,在发现bug前你都知道做了些什么,所以在发现bug时就掌握了bug的上下文信息。然而,当自动化测试失败时,你可能仅仅知道测试的ID,而并不清楚这个测试到底是做什么、它所处的位置,以及发生了哪些其他的事情。在写bug的报告前需要重建bug的上下文信息,这需要一些时间。考虑到这种情况,自动化测试应该记录一些日志,这些额外的信息对需要检查测试失败的人是很有用的。失败分析在第20、23、27和29章中讨论。
0.2.9 自动化测试是否用来发现bug
0.1.1节提到过,发现bug对于测试来说是一个好的目标,然而对于自动化的回归测试来说,找到bug却不是一个好目标。然而,在一些示例中自动化确实可以发现新的bug。如果自动化能够允许运行一些测试,而这些测试不能通过其他手段运行(因为手动运行它们需要大量的时间而且不实用),那么自动化就能增加被测软件的测试覆盖率,而且还能发现那些其他方法找不到的bug。
基于模型的测试(model-based testing,参见第9、14、24、28和29章)不仅能够在执行测试时发现bug,还能在模型的开发过程中发现bug。
当自动化测试能够运行那些依靠手动几乎不可能执行的长序列的测试时,如猴子测试、可靠性测试和探索性自动化测试,就可以发现手动测试发现不了的bug。
如果想要记录哪些bug是通过自动化测试发现的,而不是通过其他方式发现的,第11章对bug跟踪系统提出了一些建议用来找到这些bug。第27章有一些有趣的统计数据,比较了自动化测试发现的bug数量(<10%)与手动脚本测试和探索性测试发现的数量。
0.2.10 工具和技术方面
0.1.9节从管理的角度讨论了工具,同时也必须考虑到这些工具的技术方面。如果要开发自己的工具,就可以用这些工具做你想要做的事情;理想情况下,你将会得到适用于需求的最好的工具。然而,开发工具需要进行大量的工作(通常比预计的更多),于是通常需要在期望得到的功能和可以利用的资源之间做出折中。
可以尝试的是使用多个工具来达成某个目标,而这个目标无法用单独的工具来完成。在采用这些工具时,要充分挖掘它们的用途。
那些和当前测试领域密切相关的工具能保证正确使用这些领域知识,而且使用起来更容易,也更少出错。
如果你在考虑测试件该使用什么编程语言,那么就挑选那些开发人员已经熟知的并且受欢迎的编程语言。这样就能更好地得到开发人员的帮助来解决测试件中出现的问题。
你也需要仔细注意测试的环境;测试发现的失败应该是真正发生的问题,而不是因为在环境中忘记了加入一些因素。很多环境因素的设置也能自动化地进行,这也有效地减少了人为错误。
还需要关注测试和软件的同步,尤其是当被测系统并没有限定只能使用标准的GUI控件时。虽然有很多工具对于同步提供很好的支持,并不是在所有的情况下工具都能很好地解决问题。而且有时无法充分地解决所有同步问题。这也就意味着软件中有些部分是不能应用自动化测试的,至少目前不行(或许未来会出现新的技术手段来解决这些问题)。
第7、8、14、17、18、19、24、26、27和29章讨论了这些问题。
0.3 总结
自动化测试不再是件奢侈品而成为系统的必需品;随着应用程序和系统变得越来越大、越来越复杂,仅仅依赖手动测试已经无法全面地测试系统。随着技术的进步,测试也必须进行调整——而且要快。
本书描述的自动化测试的实践包含了在21世纪第二个十年开始时测试自动化的情况。这些故事中既有痛楚也有荣耀,既有失败也有成功,既有卓越的想法也有难以置信的决定。倾听这些故事并且注意这些作者学习到了什么,这样你自己的测试自动化的实践便更有可能取得成功!
(未完待续...)
相关链接:
自动化测试最佳实践 连载一
处理配置文件对于Java程序员来说再常见不过了,不管是Servlet,Spring,抑或是Structs,都需要与配置文件打交道。Java将配置文件当作一种资源(resource)来处理,并且提供了两个类来读取这些资源,一个是Class类,另一个是ClassLoader类。
当我们自己的程序需要处理配置文件时(比如xml文件或properties文件),通常会遇到两个问题:
(1)我的配置文件应该放在哪里?
(2)怎么我的配置文件找不到了?
在了解了Java加载资源文件的机制后,以上这两个问题便迎刃而解了。
对于第一个问题,答案是:请将你的资源文件放在classpath里,如果资源文件在jar中,请将该jar文件也加到classpath里面。
对于第二个问题,就得看你是使用的是哪个类(Class还是ClassLoader)来加载资源文件了,所以接下来分别讨论一下Class类和ClassLoader类对于资源文件的加载机制。
(一)用Class类加载资源文件
通过调用Class类的getResourceAsStream方法来加载资源文件:
public InputStream getResourceAsStream(String pathToConfigFile);
该方法接收一个String类型的参数(pathToConfigFile)来表示资源文件的地址,如果加载成功,则返回该资源文件的输入流(InputStream),如果失败,则返回null。重要的是,在传入pathToConfigFile参数时,有两种方式,第一种方式为绝对定位方式,即pathToConfigFile以"/"开头,此时Java以classpath为根目录,直接加上pathToConfigFile来搜索资源文件。第二种方式为相对定位方式,即pathToConfigFile不以"/"开头,此时资源文件的全路径应该为:调用getResourceAsStream方法的类的package路径加上pathToConfigFile。(在将package转为目录时将"."变成"/")
举个例子,在IntelliJ Idea中创建一个java工程,目录结构如下:
该工程里有两个resources文件夹,一个位于davenkin文件夹下,一个直接位于src文件夹下。第一个resources文件夹下有一个config.properties文件,其内容为:
name = ConfigUnderDavenkin |
第二个resources文件夹下也有一个config.properties文件,其内容为:
在davenkin包下定义ResourceLoader.java来加载资源文件:
package davenkin; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class ResourceLoader { public static void main(String[] args) throws IOException { ResourceLoader resourceLoader = new ResourceLoader(); resourceLoader.loadProperties1(); } public void loadProperties1() throws IOException { InputStream input = null; try { input = Class.forName("davenkin.ResourceLoader").getResourceAsStream("/resources/config.properties"); //also can be this way: //input = this.getClass().getResourceAsStream("/resources/config.properties"); } catch (ClassNotFoundException e) { e.printStackTrace(); } printProperties(input); } private void printProperties(InputStream input) throws IOException { Properties properties = new Properties(); properties.load(input); System.out.println(properties.getProperty("name")); } } |
输出结果为第二个resources文件夹下config.properties的内容:
原因在于(请注意ReourceLoader.java文件中的红色部分):我们给出的资源文件路径(/resources/config.properties)以"/"开头,即使用的是绝对定位方式,所以找到的是直接在classpath下的resources文件夹。如果去掉资源文件文件路径前的"/",则采用的是相对定位方式,此时应该输出davenkin/resources/config.properties文件的内容。
(二)用ClassLoader类加载资源文件
ClassLoader类也提供和Class类相同的加载方法:
public InputStream getResourceAsStream(String pathToConfigFile);
用ClassLoader加载配置文件时,pathToConfigFile均不能以"/"开头,在查找时直接在classpath下进行查找。Class类在查找资源文件时,也是代理(delegate)给ClassLoader完成查找功能的,请参考Java官方文档。
在使用Class和ClassLoader加载资源文件时,有几种区别细微的方法,修改ResourceLoader.java文件如下:
package davenkin; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class ResourceLoader { public static void main(String[] args) throws IOException { ResourceLoader resourceLoader = new ResourceLoader(); resourceLoader.loadProperties1(); resourceLoader.loadProperties2(); resourceLoader.loadProperties3(); resourceLoader.loadProperties4(); resourceLoader.loadProperties5(); resourceLoader.loadProperties6(); } public void loadProperties1() throws IOException { InputStream input = null; try { input = Class.forName("davenkin.ResourceLoader").getResourceAsStream("/resources/config.properties"); } catch (ClassNotFoundException e) { e.printStackTrace(); } printProperties(input); } public void loadProperties2() throws IOException { InputStream input = null; input = this.getClass().getResourceAsStream("/resources/config.properties"); printProperties(input); } public void loadProperties3() throws IOException { InputStream input = this.getClass().getResourceAsStream("resources/config.properties"); printProperties(input); } public void loadProperties4() throws IOException { InputStream input = this.getClass().getClassLoader().getResourceAsStream("resources/config.properties"); printProperties(input); } public void loadProperties5() throws IOException { InputStream input = ClassLoader.getSystemResourceAsStream("resources/config.properties"); printProperties(input); } public void loadProperties6() throws IOException { InputStream input = ClassLoader.getSystemClassLoader().getResourceAsStream("resources/config.properties"); printProperties(input); } private void printProperties(InputStream input) throws IOException { Properties properties = new Properties(); properties.load(input); System.out.println(properties.getProperty("name")); } } |
以上程序输出结果为(请仔细揣摩,稍不小心(比如多加了一个"/"或少加了一个"/"),就会报NullPointerException异常,表明你的资源文件没有找到):
ConfigUnderSrc ConfigUnderSrc ConfigUnderDavenkin ConfigUnderSrc ConfigUnderSrc ConfigUnderSrc |