2018年10月13日23:24:26
因为上一东家工作的原因而接触测试。原本本职是嵌入式软件,因为公司正在风口浪尖的阶段,就是一种小公司要发展成为大公司而经历的那种痛,全公司上下都忙得焦头烂额的这样的背景下,我从软件变成了“测试”小组组长。当时的心情既兴奋又担心。兴奋是因为自己一年的努力与突出表现,老板看在眼里,从而相信我有这个能力去接这个职位。现在想起来,也确实是,老板不会看错人,不会随便将一个人放在一个新建部门小组组长的职位上。但是我却时隔了4个月,离职了4个月后才深深的体会到,是真的是个非常好的机会,回想起来还是非常感谢公司的培养。现在的工作不会像之前那样,几乎是华为苹果公司的强度,至少对我来说,我是18小时在线状态,随时相应苹果客户的测试问题。这也是一个让我不自信的原因,害怕自己做不好让公司利益受损。但是现在想想还真是庆幸,我熬过了半年,压力迫使我现在处事更加从容,再复杂的问题都有始有终,总能找到一个合理的解决方法。也正是因为太忙,让一个刚刚毕业的我遇上了,我想挤点时间让自己充电,让自己沉淀,让自己多一些开发经验,这样我才有底气去领导一个小组,这是我离职的最主要原因。所以我现在才有些时间在这里巴拉巴拉这些话。
再回到正题,虽然已经离职,但也想为自己的工作,新接触的领域做一个总结,所以才接触到这本书《软件测试的艺术》。我很庆幸我在这本书中,找到我工作时的所使用方法,也让我一下子对这本书产生了好感,也对我上一家公司我的直属领导更加钦佩。本书讲解的方法都很规范,真正完全按照执行,那将是华为、阿里那样的大公司所能驾驭的。当然,很多公司不需要完完全全按照上面的经验去做,我们只需要找到哪怕一点用在我们现在所属的公司里,我觉得就足以提高开发的质量。下面就我将来可能会用到的一些方法进行总结,当然书上还有更加高级的测试理论,因为个人理解有限,不敢曲解书面意思。
中国的测试工程师是应届毕业生就可以去“胜任”的;而外国的测试是必须是一个资深研发人员才能胜任测试职位。
“测试是为发现错误而执行程序的过程”。
一个测试用例必须包括两个部分:
a. 对程序的输入数据的描述。
b. 对程序在上述输入数据下的正确输出结果的精确描述。
我们大多数程序员都不能有效地测试自己编写的程序,因为我们无法改变思维方式来尽力暴露自己程序中的错误。另外,我们开发者可能会下意识地避免找出错误来,担心受到同事、上司、客户或正在开发的程序或系统的主管的惩罚。这仅次于心理学问题所以说,更重要的是,担心程序开发者理解错了需求,导致程序出现错误。所以说,让其他人来测试程序会更加有效,也会更容易测试成功。当然,解决问题还是得程序开发的作者,那又回到了程序调试、解决bug的层面上。
这个和上面一点很相似,但是我想说的是,效果最明显的还是交由第三方专门的测试部门进行测试,而该测试部门应该是客户层面的眼光去测试。
解决任何的测试失败项,避免下次测试时显示的上一次的测试失败项,会误导开发者是新该出的bug。
这就涉及到了压力测试及循环测试了,避免因为某个功能的实现导致上一个功能失效。或者是有规律的先运行了某个功能,上一个功能就失效了的问题。
测试用例应该是被分类好,并且是一个增量的开发。这样的好处是,将来一个新的版本relese后,就可以将之前的测试用例都过一遍,有些因为重大功能改变而导致的失败,那是具体事情具体分析了。这个就是我们常说的“回归测试”。
在实际测试中,不仅仅是测试正常情况下所预期的正常结果,也应该给一些异常的测试项,测试异常的情况下,应触发的异常流程。当测试异常项后,程序表面任然可以运行的情况,也需要去排除,殊不知程序触发了某个异常的bug,在不确定的时间才复现出来。
测试一个大型软件所需要的创造性很可能超过了开发该软件所需要的创造
性。我们已经看到,要充分地测试一个软件以确保所有错误都不存在是不可能的。所以上面我说,中国与外国对测试不同的定义,我更倾向与外国的做法。
代码检查和代码走查是两种重要的人工测试方法。
代码检查与走查都要求人们组成一个小组来阅读或直观检查特定的程序。无论采用哪种方法,参加者都需要完成一些准备工作。准备工作的高潮是在参加者会议上进行的所谓“头脑风暴会”。“头脑风暴会”的目标是找出错误来,但不必找出改正错误的方法。换句话说,是测试,而不是调试。
所谓代码检查是以组为单位阅读代码,它是一系列规程和错误检查技术的集合。
一个代码检查小组通常由四人组成,其中一人发挥着协调作用。协调人应该是个称职的程序员,但不是该程序的编码人员,不需要对程序的细节了解得很清楚。
协调人的职责包括以下几点:
明白各自职责后,就可以对代码流程进行检查,任何有疑问的都应该被记录下来,并列好解决方法。以会议的形式开展,既然是开会,时间大概是30分钟到1个小时,视具体项目而定,但是开会是一个耗费脑力的活动,不易过长,不然越到后面效率越低。
与代码检查相同,代码走查参与者所持的态度非常关键。提出的建议应针对程序本身,而不应针对程序员。换句话说,软件中存在的错误不应被视为编写程序的人员自身的弱点。相反,这些错误应被看作是伴随着软件开发的艰难性所固有的。很多时候有些直属领导会直接就事论人,这样就会让软件开发作者受到打击,并且树立不好的形象,最终导致整个开发团队氛围的改变。不要在事后说控制不住或者性格这样请见谅的话,一旦不好的风在团队中刮起来,会导致团队中的成员或多或少心里有个带爆发的小宇宙,就不知道什么时候会爆发而已。所以说,态度很重要。
人工查找错误的第二种过程是古老的桌面检查方法。桌面检查可视为由单人进行的代码检查或代码走查:由一个人阅读程序,对照错误列表检查程序,对程序推演测试数据。
对于大多数人而言,桌面检查的效率是相当低的。其中的一个原因是,它是一个完全没有约束的过程。另一个重要的原因是它违反了上文提出的测试原则,即人们一般不能有效地测试自己编写的程序。因此桌面检查最好由其他人而非该程序的编写人员来完成(例如,两个程序员可以相互交换各自的程序,而不是桌面检查自己的程序)。但是即使这样,其效果仍然逊色于代码走查或代码检查。原因在于代码检查和代码走查小组中存在着互相促进的效应。小组会议培养了良性竞争的气氛,人们喜欢通过发现问题来展示自己的能力。而在桌面检查中,由于没有其他人可供展示,也就缺乏这个显而易见的良好效应。简而言之,桌面检查胜过没有检查,但其效果远远逊色于代码检查和代码走查。
这种人工评审方法与程序测试并无关系(其目标不是为了发现错误),却仍在这里谈到,这是因为它与代码阅读的思想有关。同行评分是一种依据程序整体质量,可维护性、可扩展性、易用性和清晰性对匿名程序进行评价的技术。该项技术的目的是为程序员提供自我评价的手段。
模块测试(或单元测试)是对程序中的单个子程序、子程序或过程进行测试的过程,也就是说,一开始并不是对整个程序进行测试,而是首先将注意力集中在对构成程序的较小模块的测试上面。这样做的动机有三个。首先,由于模块测试的注意力一开始集中在程序的较小单元上,因此它是一种管理组合的测试元素的手段。其次,模块测试减轻了调试(准确定位并纠正某个已知错误的过程)的难度,这是因为一旦某个错误被发现出来,我们就知道它在哪个具体的模块中。第三,模块测试通过为我们提供同时测试多个模块的可能,将并行工程引入软件测试中。
这是我所亲身经历的。当时我在开发一个平台代码,python。可以说是一个中性的系统,但是模块分工明确,开发人员分工也明确。所以当我们编写好自己所负责的模块后,自己编写单元测试代码,通过打桩,让每一行、每一个代码分支都被覆盖。而当时我们使用pytest、mock模块,结合Jenkins平台,就可以搭建出一个可视化的测试平台,查看代码覆盖,没有覆盖的代码会标记不同的颜色,很方便查阅。通过对每一行、每一个分支的覆盖测试,这样代码质量就提高了。
当然每种语言都有他相应的单元测试模块,但是思想是一致的,即不需要运行系统,就可以将模块代码中的每一个逻辑都覆盖到。
下面列举了一些常用的调试手段。
调试程序的最为普遍的模式是所谓的“暴力”方法。这种方法之所以流行,是因为它不需要过多思考,是耗费脑力最少的方法,但同时也效率低下,通常来讲不是很成功。
暴力调试方法可至少被划分为三种类型:
a. 利用内存信息输出来调试。
b. 根据一般的“在程序中插入打印语句”建议来调试。
c. 使用自动化的调试工具进行调试。
认真的思考能够发现大部分错误,甚至不需要调试人员使用调试工具。归纳是一种特殊的思考过程,可以从细节转到全局,也就是从线索(即错误的症状,可能是一个或多个测试用例的结果)出发,寻找线索之间的联系。参见下图:
演绎的过程是从一些普遍的理论或前提出发,使用排除和精炼的过程,达到一个结论(错误的位置),参见下图:
在小型程序中定位错误的一种有效方法是沿着程序的逻辑结构回溯不正确的结果,直到找出程序逻辑出错的位置。换句话说,从程序产生不正确结果(如打印了不正确的数据)的地方开始,从该处观察到的结果推断出程序变量应该是些什么值。在头脑中,从这个位置开始逆向执行程序,重复使用“如果程序在此处的状态是这样的,那么程序在上面位置的状态就必然是那样的”过程,就能很快定位出错误。使用这个过程,可以确定程序中从状态符合预期值的位置点,到第一个状态不符合预期值的位置点之间的范围。
最后一个“思维型”的调试方法是使用测试用例。这可能听起来有些奇怪,因为从本章一开始就将调试和测试区分了开来。然而,考虑下面两种类型的测试用例。供测试的测试用例,其目的是暴露出以前尚未发现的错误.供调试的测试用例,其目的是提供有用的信息,供定位某个被怀疑的错误之用。两者之间的区别是,供测试的测试用例会“胖”一些,因为我们尽量使用较少数量的测试用例来涵盖较多的条件,而供调试的测试用例则“瘦”一些,因为每个测试用例仅需要覆盖一个或几个条件。换句话说,当发现了某个被怀疑的错误的症状之后,我们需要编写与原先有所变化的测试用例,尽量确定错误的位置。实际上,这种方法不是一个完全独立的方法;它常常结合归纳法一起使用,以获得进行假设和/或证明假设所需的信息。它也可以和演绎法一起使用,以排除有嫌疑的原因,提炼剩下的假设,并/或证明假设。
再一次申明,分析的过程是很艰难的,但是找到的答案为改进后续的编程实践提供极其宝贵的价值。值得警惕的是,绝大多数的程序员和编程机构都尚未使用这种方法。