敏捷软件开发是近些年来比较热门的话题,《敏捷宣言》四条主要精神和十二条基本准则概括了敏捷开发的基本思想。围绕着这些基本概念和思想,产生了一系列的轻量级方法,如:极限编程、测试驱动开发、Scrum、特性驱动开发等。虽然具体名称、过程和侧重点不尽相同,但是相对于非敏捷的开发方法而言,它们都更强调面对面的沟通、团队不同角色之间的紧密协作、频繁交付新的可用的软件版本、紧凑而自我组织型的团队等。敏捷开发只是提供了一个思想和方法论,而要在实际的工程中正确运用它,并真正显现出它的优点和产生实际的效果,这对于每个团队而言一开始都是一个挑战,尤其是对那些那些习惯了传统瀑布模式的团队。
敏捷是整个团队的敏捷,不只是团队中某个角色或者某个阶段的敏捷,开发、测试和项目经理等所有角色都要敏捷起来。敏捷方法的采用对团队每个成员都提出了新的挑战,尤其是测试人员。之所以这样说,是因为相对于传统的瀑布模型,敏捷开发所要求的频繁交付,给测试所留出的时间更为紧迫,要求测试人员更早的介入和更及时地完成测试任务。如何在这么短的时间内完成测试的计划和实施呢?如何有效地避免回归问题的出现?手工测试人员如何能更好的融入到敏捷团队?等等问题接踵而至,这都需要需要测试人员不断的思考和尝试。
无论是哪种开发模式,软件的开发过程都可以归结为:人、工具和过程这三个因素,三者的有机结合才能更高效的完成任务。有人会说:《敏捷宣言》四条主旨精神的第一条就是“个体和交互重于过程和工具”,工具还有那么重要吗?回答是肯定的,工具很重要,这条主旨所提到的是“重于”而不是不要。为了支持敏捷开发,Visual Studio 2010(以下简称为VS 2010)应用程序生命周期管理中引入了MSF for Agile Software Development v5.0过程模板,用于辅助敏捷团队在实际工程中进行敏捷实践,它支持Scrum敏捷开发过程框架。本文将从工具角度出发,介绍Visual Studio 2010如何帮助测试人员更胜任敏捷项目中的测试工作。对于工具与人的关系而言,好的工具应该是将人从重复和机械的劳动中解脱出来,让人有更多的精力和时间花在有创造性地劳动上,而由工具去完成将繁琐和冗余的事务性操作;而对于工具和过程的关系,工具是过程能够得到确实落实和准确执行的基石,很多时候我们总是依赖于人去执行某个过程或者流程要求,但人的执行往往带有一定不稳定性和主观性,而工具则可以帮助我们准确客观的执行。
团队有效协作的基石——Team Foundation Server
敏捷开发强调人与人之间的有效沟通和紧密的团队协作。对于测试团队和测试人员而言,首先应该需要考虑的是:如何让测试工作更有效的集成整个敏捷开发的活动中去?而不是将测试工作仅作为一个“附件”或者可有可无的副产品。当然,这会受到团队组织形式和开发过程的限制,例如:采用功能小组模型的团队,所有角色成员(PM、开发人员、测试人员)隶属于同一个功能团队,客观上其沟通就更为方便;而对于采用纵向按职能划分团队的公司而言,测试和开发在隶属关系上是分开的,相对在沟通上障碍就会更多些。无论是哪种组织形式,好的工具能帮助促进和统一各个角色间的信息互通和共享,而不是要让他们彼此之间更为孤立、工作在各自的一亩三分地(Silo)中。Team Foundation Server 2010(以下简称为TFS 2010)就是这样的工具,作为整个团队协作的核心,它统一了团队不同角色信息、实现了信息之间的有效互联互通、彼此之间的共享和关联,例如:TFS 2010定义6种默认的工作项类型,如下图所示。
图1:TFS 2010定义6种默认的工作项类型
其中,Test Case和 Shared Steps是2010专门为测试新加入的。不要小看这些工作项,它们之间有着丰富的关联关系,这种关系背后所代表是角色之间的关系。对于测试而言,它将测试和团队紧密的结合在一起。例如:Test Case工作项用来详细定义和管理测试用例,它还可以和User Story相关联,也就是将测试和用户需求进行了关联,用户可以从需求追溯到覆盖的它的测试用例,这背后体现的是测试人员和需求人员/PM的协作;Test Case还可以与Bug关联,通过这种关联可以挖掘出哪些 Bug被测试用例覆盖,哪些还没有,这种关联体现了测试人员与开发人员的写作,如果是自动化测试用例,则体现了手工测试人员和自动化工程师的协作;Bug 还可以可以和签入集(Change-set)关联,可以找到为了修复Bug,开发人员修改过哪些产品代码,这体现了测试人员和开发的关联。
敏捷开发频繁的迭代和较短的迭代周期,对项目管理的精确性、透明性和可见性都提出了更高的要求,尤其对于那些项目复杂和人员较多的团队。Task是另一个重要的工作项类型,它用于管理开发过过程中的所有任务项,包括:开发、测试以及需求等任务,统一管理开发中的所有任务,统一计算项目的开销和剩余工作量等。例如,项目的燃尽图就是由它产生出来的。现在,人们虽然在理论和概念上已经非常认同软件测试的在工程中的重要地位,但在具体实际操作中,测试却仍然被看作是低于开发和需求分析等的“二等公民”。当然这是由于多方面的综合因素造成的,从管理技术角度讲,这是由于测试工作本身缺乏可度量性和可见性,从导致了测试工作的透明性的缺失,团队往往看不到测试工作的进度和所带来的成果,从而意识不到测试的真正作用。对于测试人员自身而言,缺乏可度量性也让自己无法对工作进度准确把握,进而失去了对自己工作的目标感和认同感。将测试工作同其他工作一样的用 Task工作项管理起来,增加了它的可度量性和可见性。将测试工作和其它任务一起统筹,时刻确保测试被作为整体中的一部分进行考虑,所有的测试任务都被作为Task工作项记录下来,例如:编写测试计划、设计测试用例、自动化测试用例等等,每项任务都有三个默认时间估计数据需要填写,它们是:Original Estimate、Remaining和Completed,分别代表了任务的预估时间、剩余工作量和完成工作量。
为了增强敏捷过程的透明性和可见性,TFS 2010定义了很多的报表和仪表板(Dashboard),它们会自动生成各种报表,以可见的方式描述敏捷项目的健康状况,这其中就有很多反映测试工作的报表,如下面所示。Stories Overview展示了用户故事的进展情况,包括了每个用户故事的测试用例覆盖数量和执行结果,以及相应的Bug数量;Test Dashboard显示了测试用例的状态,包括正在设计的用例以及设计完毕可以执行的用例数量,现实当前Bug的状况,包括未被修复和以修复Bug的数量。
图2:Stories Overview展示了用户故事的进展情况
图3:Test Dashboard显示了测试用例的状态
集成测试环境——Microsoft Test Manager
在过去的十几年中,为了适应了软件项目的复杂度和规模的不断膨胀,软件开发工具和框架得到了长足的发展,而测试工具则始终是块短板,特别是对于那些需要手工完成的测试任务而言,进展就更为缓慢,例如:现在很多团队仍然使用Word或者Excel这样“原始”工具来管理测试用例。通过对业界的调查和分析,我们发现70%的软件测试工作仍然是通过手工或者简单的脚本来完成的,在测试团队中不具备编程能力和仅有基本脚本编写能力的测试人员仍然是测试的主力。
要让你的项目敏捷起来,对于那些仍以手工测试为主的团队而言是一个非常大的挑战,如何提高手工测试工作的效率将是实现敏捷的成败关键。在VS 2010中,微软首次为测试人员设计了一款专用的集成测试环境,称为微软测试管理器2010(Microsoft Test Manager 2010,以下简称为MTM)。之所以称之为集成测试环境,是因为MTM的功能涵盖了测试计划、测试用例、手动测试用例的执行和录制回放、自动测试用例执行、创建信息丰富的Bug、验证Bug、以及与测试实验室管理相关的对策是自动化相关的功能等。下图展示的是MTM测试计划的操作界面,它以树形的层次结构来组织测试用例。
图4:MTM测试计划的操作界面
《敏捷序言》强调:“可工作的软件重于完备的文档”,那么是不是意味着敏捷测试也不需要测试计划呢?当然不是。敏捷的本质是要去除软件过程所有造成时间浪费地方,不需要的是那些动辄就几十或上百页的文档。敏捷对文档要求是要简明扼要,一两页列出测试要点计划还是必须的,较短迭代周期(1-4周)也不可能要求文档面面俱到。敏捷需要更快的对功能进行验证,是不是不需要编写测试用例直接根据用户故事或者功能需求进行探索性测试就可以了?当然也不是。功能需求和用户故事勾画出的是一棵大树躯干和主要枝杈,而那测试用例则不但要准确描述出躯干和主枝,还要描述出细小的枝杈和绿叶的正确位置。从某种意义上讲,测试用例在敏捷中的作用和地位应该更为加强,它扮演着详细功能文档的角色。功能需求和设计文档可以简单,但测试用例可不是这样,相反我认为敏捷对测试用例的设计和管理要求更高。
每个迭代周期,团队都会专注于实现不同的产品功能,用户故事虽然描述了功能的内容,但并不足以覆盖所有相关的内容。很多由用户故事展开和关联的功能一般在文档中会体现出来,需要测试人员在早期围绕着用户故事测试展开需求文档测试(需求评审),已明确那些未严格定义出来的内容,以测试用例的形式明确和记录下来。由1个简单用户故事就有可能扩展为1+N用户可能执行的执行片段,也就我们测试用例。当你有M个用故事,需要M个迭代周期来完成产品,那么就会有 ( M + N1 + N2 … + NM) 个测试用例,不把它们落实到笔头上,很容易就会丢失一些重要的测试细节。此外,在敏捷方法中需求变化比较快,随着多个迭代的深入,文档的变化往往赶不上产品功能的变化,这时唯一能够赶上这个变化的只有测试用例,应为只有它准确地反映了产品的变化,否则测试用例就是无法通过的。
图5:测试用例
在MTM 中,测试用例被分类至各个测试用例集,结构十分清晰。测试用例只是逻辑上从属于某个测试用例集,并没有物理从属关系,即一个测试用例可以同时被分在多个测试用例集内,比如某个测试用例性质上是一个性能测试,但是由于该用户故事的诉求就是性能改进,我们也就很自然得可以将其作为该用户故事的验收测试,此时我们就可以将此测试用例添加到验收测试和性能测试两个测试用例集中;另一个例子是给每个用户故事都定义了不少测试,这些测试用例都应该能在用户故事测试用例集下找到,但是这些测试既可能是手动测试也可能是自动化测试用例,所以它们又会被本别归类至这两个测试用例集。在这种逻辑分类的支持下,我们可以很容易的根据需要指定运行测试集中一部分测试用例。比如,我们可以定义一个签入测试的测试用例集,挑选最基本的若干个测试置入其中,这样在每次签入前通过运行这个测试用例集就能帮助我们确保签入的代码不至于破坏最基本的功能,即保证了版本随时可运行可测试,这无疑为测试带来了更多的方便。具体如何创建测试用例集的结构,团队可以根据自己项目的特点,灵活运用此功能,制定分类规则以提高工作的效率。
很多测试团队仍然在使用Word或者是Excel管理测试用例,有些是使用专门的测试用例管理工具,使用独立的数据库来存储测试用例信息。MTM相对于这些工具的优点在于,它的所有数据都是存储在TFS上,测试用例使用的是Test Case工作项。由于同存储在TFS 上,所以可以轻松的实现与其它数据项的关联,例如:在上一部分我们介绍的不同类型工作项之间关联,此外还可以把Test Case与代码关联,即将测试用例与自动化测试代码关联。这样在MTM中,也可以直接管理和运行自动化测试用例,使MTM兼具了管理手工测试用例和自动化测试用例的能力。
探索性测试(Exploratory Testing)是测试人员在对被测试系统的功能进行不断了解和学习的过程中进行测试,包括:设计测试用例、执行测试、以及汇报测试结果。与传统的测试相比,它不需要事先定义好的齐备的测试文档,更强调测试人员在对系统不断地学习中,边了解边测试,它在很大程度上给测试人员更多地自由和想象空间,充分发挥他们的创造力,在不断地学习中找到测试的灵感和快乐。这种测试的灵感和快乐对于组建和培养一支热爱测试的团队是非常非常重要,它会让测试人员觉得自己不是执行重复测试劳动机器,而是一个有着创造力和灵光的团队成员。MTM也支持探索测试功能,用户可以使用MTM创建一个仅有一个测试步骤的测试用例,然后执行它,Test Runner工具会辅助执行手动测试。它会记录下所有用户的操作,一旦发现有Bug时候,可以直接选择‘Create exploratory bug’直接创建一个Bug。
图6:创建一个Bug
Bug是测试工作最重要的产出之一,也是测试和开发人员之间重要接触点。每个提交的Bug都应该详细记录下如何重现(reproduce)的步骤,这是衡量Bug质量的重要因素之一。因为不可重现的Bug是没有意义的,只会耽误开发人员和项目经理的时间。偶尔出现不可重现的Bug还是可以理解的,但如果经常出现,那就会引来开发人员的抱怨和不满,久而久之会造成开发和测试之间的不信任。好的Bug应该是有清晰和详细的重现步骤,期望的结果和实际得到结果,并提供尽可能多的信息,例如:出现问题的产品版本编号、语言、操作系统的版本以及日志信息等。大多数情况下,用文字进行描述的内容就可以了,但如果能配上一张问题现场截图,或者对于更为复杂的Bug,配上一段小的录像,这样的Bug会给开发人员快速诊断和修复产品问题带来很大帮助,大大提升测试和开发人员之间的协作效率,避免了不可重现Bug在开发和测试之间推来推去的“Bug乒乓”现象。然而要收工创建这样一个信息丰富的Bug,是需要很多时间的。MTM提供了这样的功能为帮助测试人员创建这样高质量Bug,它实现了多种诊断数据适配器(Diagnostic Data Adapters),在测试确认Bug的过程中,这些适配器会在后台运行收集大量的数据,包括:执行操作、系统配置、IntelliTrace已经操作过程的录像等,当测试人员要创建一个Bug时,这些信息会被自动添加的Bug中,如下图所示,测试仅需填写很少的内容就可以创建好一个信息丰富的Bug。
图7:信息丰富的Bug
关于作者
周京生,微软亚太研发集团服务器与开发工具事业部软件开发测试工程师,目前主要负责Visual Studio 生命周期管理工具对C/C++支持工具的测试工作。自2006年加入事业部以来,一直致力于架构工具的设计开发以及如何使用架构工具促进软件开发生命周期管理。周京生先后参与了 Visual Studio 2005 SDK 和 Visual Studio 2008 的测试工作。在刚刚发布的Visual Studio 2010 旗舰版中,周京生和团队共同完成了多种UML建模工具的开发和测试工作。