功能点算法及在软件测试中的应用

——划分逻辑事务

在前一篇文章我们讲到,“逻辑事务”是统计功能点指数的最小单元,所以进行科学的划分,对统计的正确性非常重要。另外,划分逻辑事务其实也是一个需求分解的过程,测试工程师可以通过这个过程来分析项目需求,让需求分析工作更加标准化,同时也降低沟通成本,大家围绕逻辑事务进行讨论。

逻辑事务一般描述的是用户的行为,所以命名一般都是动宾结构,例如“注册淘宝会员”、“查看宝贝的详情”。也有少数的逻辑事务是由一些定时程序产生的,例如“同步用户的最新信息”。有的项目会在需求文档里面专门描述一些“业务规则”,注意,规则不是逻辑事务,规则一定是依附与某个逻辑事务的,例如“不准注册同名的会员”这个规则其实是属于“注册会员”这个逻辑事务。

在以数据库为基础的WEB应用中,逻辑事务一定是对某项数据进行的增删改查操作,也就是我们常说的CRUDL的其中之一。CRUDL分别代表:

  • Create 创建新数据(注册会员)
  • Read 读取数据的信息(查看宝贝)
  • Update 更新数据(保存收货地址)
  • Delete 删除数据(清空购物车)
  • List 列出一批数据(各种DataGrid)

下面我们对这5种逻辑事务分别进行分析,并且结合具体的案例来看看划分的规则。如果逻辑事务划分正确,后面的计算出现误差就不会太大。

Create

每个WEB应用程序,都是从创建数据开始的,比如“发布新宝贝”、“注册新会员”,创建数据会在数据表中新增记录。创建数据一般由用户在页面上点击按钮触发,也可能是在请求一个URL的时候触发。

有时候,用户在一个页面点击“新建”,会同时新建两个数据对象,比如注册淘宝会员,会同时创建一个支付宝帐号,这里不能算做2个逻辑事务,而只有1个,这个逻辑事务的“实体”是2个。

Read

读取数据的逻辑事务基本都叫“查看XXX的详情”,WEB程序会根据主键,把某个数据对象select出来,把各个字段的值显示在页面上。在分析Read类逻辑事务时,要对页面进行分块分类,因为现在很多WEB页面,都不是单纯显示一个数据,而是用i_f_r_a_m_e的方式,把相关对象都显示出来,这里每个对象都是一个独立的逻辑事务。

注意,在一些列表页面,比如“我购买过的商品”,是用数据列表的方式,展示出很多商品,这不属于Read类逻辑事务,而是属于List,后面会讲到。Read类一般都是展示单体。

有些Read类逻辑事务会出现,不同身份的用户查看同一数据对象时,有不同的输出,比如VIP用户查看商品时有价格优惠。这里不能算作2个逻辑事务:“普通用户查看商品”“VIP查看商品”,而应该算1个。“用户的角色”只是一个输入DET。不过,如果普通用户和VIP用户查看商品,完全是两个不同的页面(View不同),那就要算2个逻辑事务了。

Update

这类逻辑事务是对已经存在的数据进行更新,一般叫做“保存XXX信息”,不过在某些场景,Update逻辑事务有很多其他的命名,例如“为订单付款”,实际上是更新了订单对象的状态,因此归于Update类。

在一些信息的保存页面,例如“保存我的收货地址”页面,用户需要先打开某个收货地址的详情页面,然后再进行保存,那么这个详情页面,要算作一个Read类逻辑事务,用户点击按钮保存,算作一个Update类逻辑事务。

Delete

删除类逻辑事务出现的不多,一般都是用户点击“删除”按钮,把一个或者几个数据对象删除。老规矩,如果用户一次点击,删除一个对象的同时,又级联删除了这个对象相关的另一个对象的话,只算作一个逻辑事务,实体是2个。删除时一般都会弹出一个对话框,让用户确认,这个确认不算逻辑事务。

List

此类逻辑事务最常出现的形态是DataGrid数据表格,例如上文说的“列出我购买过的商品”。这个控件在WEB应用程序中被使用的非常多,它用一种格式在一个页面展示出多个数据对象,并且把这些对象的关键属性(名称、类型)显示出来。除了DataGrid,树型控件也是一个List类的逻辑事务。此外,页面中展示对象的下拉菜单、RadioButton,也要作为单独的逻辑事务来计算,不过前提示,它们显示的是数据对象,如果是“男/女”这样的RadioButton,不是逻辑事务,而购买商品时,选择的“收货地址列表”则是逻辑事务。

有一些DataGrid控件,支持用户直接在表格里修改数据,这里的修改功能要单独作为Update类逻辑事务计算,与上文有一点不同的是,不需要另算Read类逻辑事务了。

到这里我们对这5类逻辑事务都分析清楚了,大家划分逻辑事务时,还要明确一个原则,每个逻辑事务的实体个数、输入DET个数、输出DET个数都不能是0,否则就不算逻辑事务。例如,有的页面上设计一个按钮,用户点击后,跳转到另一个页面,这种单纯的跳转,不是逻辑事务。逻辑事务都是对数据对象的操作。大部分情况下,数据对象都在数据库中,也有一些情况,数据对象是文件对象,比如上传图片,图片本身就是实体对象。

以上所列出的,都是常见的逻辑事务案例,在真实项目中,还可能会遇到一些其他的情况,大家只要根据逻辑事务的判定原则,进行推理,就可以很好的把逻辑事务划分清楚了。

最后,要说一下测试用例设计。非常相似的,测试用例也是围绕逻辑事务来设计的。在组织用例分类时,基本遵循“项目-功能模块-逻辑事务”这种目录结构。每个逻辑事务的用例,都拥有类似的前置条件、操作步骤、校验方法,所以组织在一起设计,是非常科学的。

Q:逻辑事务分解对于那种“增删改查”类型的功能点比较适用,但是流程类的功能点,就不合适了吧?
A:就拿交易流程来说好了,我们在设计交易流程的TC时,是把下单、付款、发货、确认等操作,分别进行设计的,这些操作,其实都是单独的逻辑事务,实际上,开发在设计程序的时候,也是分开做的,不可能全写在一个函数里面。

Q:我发现有的逻辑事务,比如点击一个按钮以后,程序既做了查,又做了改,按照你Part2里的分类,是不是应该算两个逻辑事务。
A:这个怪我没说清楚,这应该是算一个逻辑事务,他可以同时包含多个CRUD的action。

Q:我们以前设计TC很多都是基于一个页面的,现在按照这种方式,一个页面会分解成多个逻辑事务,这样感觉会比较零散。
A:测试设计和测试执行是有区别的,测试设计的目标是把被测系统分析清楚,并不是编写出完整的执行脚本,实际上在测试执行过程中,有经验的测试工程师是会把几个逻辑事务放在一起测,效率极高。

我们仍然会分别对CRUDL这五类逻辑事务进行分析,因为他们的输入和输出的特点,各有不同。不过,对于“实体”的计算,各类逻辑事务的计算方法相同,所以先单独讲一下实体。

淘宝系统里存在下面这些实体:会员、宝贝、交易记录、宝贝类目、评价记录、店铺等等。实体的英文原名是Entity Type,也就是我们平时讨论中经常出现的“对象”,经常接触代码的工程师会在代码里发现很多“xxxDO”,比如UserDO、OrderDO,这些和实体是完全对应的。另外还有一个简单的办法来识别实体,就是看数据库的表,一般一个实体肯定至少对应一张表,比如会员这个实体在数据库里,必然有一张users表。

分析一个逻辑事务有哪些实体,是分析的第一步,也是最重要的一步。实体越多,开发和测试的复杂度越高。从MkII算法里也能看出,实体的系数是1.66,远高于输入和输出。另外,分析实体可以帮助测试工程师搞清楚,这个事务的范围,对事务有一个全局的概念。分析完后,测试工程师一般会这么说:“哦,这个事务跟这几个对象有关!”在新人学习和测试设计评审中,实体的分析也能起到非常大的帮助作用。

下面开始分类讲CRUDL的输入和输出:

Create

注册会员、发布新宝贝,这都属于Create类型逻辑事务,这类事务一般都有一个内容较多的“表单”,里面有一些输入框、checkbox、radiobutton和一个确认提交按钮,这里的每个控件,都要计算为1个输入DET。需要注意,radio控件一般会有多个选项,不能算多个输入,而只能算一个;提交按钮也要算1个输入。除了这些页面控件类输入,还有一类输入,是“隐含”的。比如卖家在发布新宝贝时,卖家自己的userid就是一个输入,因为在发布成功后,这个宝贝会拥有卖家的userid,只是这个id并不需要卖家填写,而是放在系统缓存里。虽然是隐含输入,却也参与了逻辑运算,因此要计数。这是Create的两类输入。

Create事务的输出一般会有以下一些信息,“注册成功”、“此会员名已被人注册”、“密码太短”。当用户试图执行这个事务,系统给用户所有可能的提示信息,都要记为一个输出。有些输出跟某个输入,有直接的逻辑关系,比如“此会员名已被人注册”只跟“会员名”输入框里所填的信息有关,有的输出,则是由好几个输入组合在一起,才能出现,分析的时候,需要弄明白,不过这点区别不影响计数。

如果把逻辑事务看成一个“函数”,那么输入就可以抽象为函数的输入参数,而输出就是函数的所有可能性的返回值,以及函数抛出的各种异常。后面几类逻辑事务,也可以抽象为这种定义,大家后面自己推理试一试。

Read

Read类事务是读取一个对象的详细信息,比如我们查看一个宝贝的详细信息。它的输入个数一般比较少,其中最基本的,就是这个对象的主键id,比如宝贝的id。如果不同类型的用户查看宝贝时,会有不同的显示信息,那么用户的userid和用户类型这2个要记为输入。如果宝贝的某些属性会影响显示,比如鞋城宝贝会有个图标,那么输入也要+1。其实如果业务逻辑复杂,输入也不少。我们可以把这些输入抽象的称为“影响对象显示的一些属性”。

一般来说Read的输出都比较多,这个对象能显示在页面上的属性,都要记为输出,比如“宝贝名称”、“价格”、“颜色”等等。除了文字类,图片也是输出,比如宝贝的缩略图和表示宝贝属性的一些小图标,都算。另外,有的图片和Label有链接,这里的链接要单独算输出,因为一个纯文本,肯定没有一个附带http链接的文本信息量多。

Update

Update事务的输入和输出数量可多可少,关键看系统Update的规模,比如“编辑宝贝”跟“发布新宝贝”相比,输入输出的数量都非常多。像“修改我的登录密码”这样的,数量就非常少了。Update事务的输入输出识别,与Create类非常相似,因为大部分Update事务也是表单提交的方式。

这里需要注意的是,系统中会存在一些“不起眼”的Update事务,分析时千万别漏了。比如大部分会员有多个收货地址,然后在列表中有一个鼠标悬停出现的“设置为默认收获地址”的按钮,当用户点击的时候,只是修改收获地址的一个属性,非常的隐蔽。这也是一个逻辑事务,它的输入是用户id,收货地址id,输出只有1个,就是点击按钮后,系统提示修改成功,非常简单,但是不要遗漏。

Delete

Delete类事务的分析相对简单一些,多数删除功能,都是直接对数据进行删除,因此输入一般就是数据主键id这1个。不过,有一些数据在删除前,需要先做一些逻辑判断,看看能不能删,这样输入就多了,相应的实体也会增加。比如,“已经发布的宝贝不能删除”,那么“宝贝发布状态”就是1个输入,“已经有交易的宝贝不能删除”,那么实体就不仅仅是宝贝,还有交易记录,输入也要增加“交易记录id”;如果规则更复杂“有未完成交易的宝贝不能被删除”,那么输入还要增加“交易状态”,依此类推。

List

List事务是一个重点,最后讲,它的输入输出计算比较复杂,而且多变,所以大家不仅要理解我下面讲的东西,还要能自己推理,分析自己实际遇到的各种List。List事务实质上跟Read很像,不同在于Read只看1个对象,List要看多个。首先看最常出现的List事务:DataGrid,这种控件一般是一个二维表格,它的输入与Read类似,比如我的订单列表的输入是会员userid,我的已买到订单列表还要增加订单类型这个输入,我的近3个月已买到订单再加订单时间,等等。有些列表上方,会有查询功能,比如按照名称查询,这些查询项会影响列表的显示数据,因此是输入。大家如果想象一下这个列表对应的sql语句,就好理解了,where后面跟的都是输入。

DataGrid的输出比较直观,每一列就是一个输出,对应sql语句里的select后面的字段,注意,有些列显示的不仅仅是文字,还有图片,颜色,这些都要单独计数。

对于DataGrid来说最纠结的要属“翻页”和“排序”功能,这究竟是算输入还是输出呢?经过推理我们发现,翻页功能对显示的数据是有影响的,比如我翻到第二页(假设每页10个数据对象),很多程序会控制从数据库里读取的数据,只取出第11到20的数据,也是在sql的where后面加条件,所以,翻页属于输入的计数,流行的翻页一般是“上一页”“下一页”这样的按钮或者是直接输入数字翻页,只要出现1种,输入+2,要是两种都有,+4。再说说排序,排序对应的sql标记是order by,不是select也不是where,比较另类,究竟算哪边合适呢?在MkII的相关资料里也没有找到答案。通过比较发现order的操作与where更接近一些,相似度更大,都是影响数据的检索范围,因此我们把排序也认定为输入。DataGrid如果有1列提供排序功能,那么输入+1,多列的话,依此类推。

前文曾经提到过,显示数据对象的radio控件,也是List类的事务,它的输入,就是显示这些数据的条件。比如买家购买商品时,有个收货地址的radio控件,因为是“我的”地址,所以userid是1个输入。输出就要看这个radio展示了数据对象的哪些属性,有几个算几个。比如收货地址的姓名、省、市、区、邮编,这些都是不同的字段,但是在radio里全部拼在一起,要算5个输出。

还有一类控件使用也较多,就是树形控件,这也是List类的逻辑事务。输入算法就不说了,同上,重点说输出。树形控件里的每个节点,一般都是一个数据对象,节点会有名称、颜色、加粗、小图标这么几种显示元素,用来表示数据对象的属性,这些都是输出,单独计数。树形控件有特殊的展开、折叠功能,这个不太好分类,我们直接规定,输出+4,因为在折叠上我们需要投入一点测试成本。如果一棵树里展示了两种数据对象,别忘了实体要+1。

List的例子我们就讲3个,大家还有可能遇到各种五花八门的List逻辑事务,只要掌握这个原则:输入会影响显示数据的范围,而输出是展示数据的各个属性。大家只要掌握规律,细心推理,遇到问题方可迎刃而解。

功能点分析法应用在软件测试中,它最核心的价值究竟是什么呢?

让我们先看看软件开发,这是跟测试离得最近的工作类型。开发工程师工作大致可以分为“设计”和“编码”两步。设计一般是使用UML语言,借助类似于Rose这样的工具,绘制一些UC图、类图、ER图等等。这些设计图决定了最终的编码该如何实施。另外,当其他的开发工程师需要了解这个project时(例如评审),ta会先看设计文档,从设计文档中掌握开发思路,然后再阅读代码,了解细节。

由于UML语言中,包含了大量的约定和规则,因此开发工程师只需要花费较少的工作量,就能表达出充足的信息。而阅读UML设计文档的其他人,也能很快从UML设计中了解设计人员的思路。试想一下,如果让读者直接看代码,需要花费多少倍的时间,才能达到相同的目的。这就是设计模型的价值,不仅帮助设计人员自己整理思路,也帮助其他读者快速交流信息。

对于软件测试来说,也有“设计”和“编码(实施)”两个阶段的工作。设计是我们设计测试用例的过程,也就是我们考虑如何做;实施就是我们执行测试的过程,有时是手工执行,有时是写脚本让计算机执行。因此,测试用例是我们的“设计文档”,是我们交流测试方法,评审测试方案的核心。但是只依靠测试用例,我们感觉存在很多问题:

  • 测试用例数量多,难以阅读
  • 测试用例结构五花八门,风格迥异,不同团队间不好交流
  • 测试用例很难清楚表达需求逻辑,每次用例评审,要花费大量时间,讲解需求逻辑
  • 测试用例描写的是测试细节,较难看出测试的思路和重点

在这种情况下,我们需要一种测试设计模型,用来解决上面那些问题。事实上,测试设计模型不是唯一的,我们允许团队中使用各种设计模型来设计测试用例。以前我们曾经用UML来设计,这是一种设计模型。不过UML开发工程师用起来合适,我们测试用就不是特别合适,毕竟它的优势,是描述程序的开发实现。另外,设计模型和测试用例模式,应该是成对出现的,也就是说,用什么样的设计模型,就应该有合适的用例模式与之对应。一成不变的用例模式,其实是不存在的,它必须要紧跟设计模型。

这就是我们选择功能点分析算法的最主要目的:寻找一种新的设计模型,改善我们的测试用例设计过程,精简测试文档(因为模型可以包含很多信息),让测试团队用一种相同的设计模型进行工作,减少沟通成本,更好的支撑我们的业务测试。

现在我们面对的,是互联网软件产品,这一类软件的特点,不同于传统的应用软件,互联网应用软件多使用BS结构,MVC的开发模型,有庞大的数据库作为支撑,需求和用户界面多变,市场竞争激烈,等等。在这种条件下,测试工程师往往没有太多时间设计用例,而是要快速的面对大量新需求和需求变更,第一时间找出程序的bug,这才是王道。

下面,我们讲一下,怎么样使用功能点分析的方法,来设计测试用例。

如part2所说,我们拿到一个project,首先需要把它拆分成逻辑事务,然后针对每个逻辑事务,讨论使用何种测试方法。有些事务属于核心事务,必须要重点测试,要设计完整的用例,有些事务只需编写一个简单的check list,就足够指导测试执行了,有些事务甚至根本不用写任何文档,测试工程师拿到手也知道该怎么测试。在这里我不想再回答“一个完全不懂的测试新人,看不到用例,该怎么测试?”这样的问题。测试新人正式上岗之前,必须接受测试技术培训,和project的业务培训。如果是跨团队合作(其实这种场景很少),那么PTM也要出面先做一些测试方案的讲解,绝对不能把测试用例直接扔给其他工程师。

这里我们推荐使用freemind或者xmind这样的思维导图软件,来做功能点分析。root node一般是project的名称,比如购物车。然后下级node是各个模块的名称,然后就是逻辑事务的名称。本文选了一个逻辑事务作为案例:买家在宝贝详情页面点击购买。通过对这个事务的功能点分析,再推导出相应的测试用例。事实上,淘宝测试团队的twork小组,正在开发通过freemind图,自动生成测试用例的功能,所以在下面的讲解中,我会不断比较,freemind图和最终生成的用例。

首先在逻辑事务的node下创建:输入、输出、实体3个node,先列出所有的实体。实体对用例设计并没有什么影响,只是告诉读者,这个事务跟哪些对象有关,这样可以清楚的界定用例范围。如下图:

“01点击立即购买”是我们今天要讲的事务,02~06也是事务,但是今天不会讲到。使用twork把这个设计图导入以后,将会产生对应的目录结构,注意,一直到逻辑事务这一层,都和设计图相同,再往下,会根据设计的不同有所变化,而并不产生“输入”“输出”这样的测试集。如下图:

下面重点要讲输入,这和测试用例的设计有很大关系。这个事务的输入比较多,不过我们如果分类来看,就会比较清楚。首先看最上面那3个实体的主键id,这3个输入是必须要参与程序的逻辑运算的,但是与测试用例无关。如下图:

有一类输入,比如买家状态,会有很多枚举值,这些枚举值会产生非常优先的判定,比如说,一个被处罚的买家,是不能购买宝贝的。这一个条件就可以直接产生一个确定的结果,这些结果,一般是用页面文字的方式告知用户,所以要算作“输出”。注意:输出的项,不一定都在“输出”这个node下面,而是有很大一部分,会挂在输入项的下面,表示和输入的逻辑关系,这种关系也是设计图中的重要信息,如下图:

在导入twork以后,会在逻辑事务的测试集下,产生一个叫做“买家状态”的测试集,这些枚举值将变成输入条件,而后面的输出结果,将变成期望结果。如下图:

 

还有一种输入项,比如页面表单的输入框,会产生一堆输出:“不能为空”“不能超过20字符”等等,在设计图中,我们可以把这一堆输出,直接挂在输入项下面,这样,也会产生一组用例,也就是我们常说的页面校验。

上面所说的,是输出和输入紧密关联的情况,产生的用例比较简单。除此以外还会出现更复杂的情况,当多个输入组合在一起的时候,才会产生一定的输出。这时,就需要把这些有逻辑关系的输入组织起来,在设计图里单独建一个node,注意这个node上不要标记Input,因为它不是一个输入项,而只是一个分组。真正的输入项在下面。如下图:

根据这一部分的设计,会生成一组比较复杂的用例,每个输入项,会成为一列,这里有4个,就是4列,另外再加1列“期望结果”。这是twork中一种新的用例编写模式,叫做测试数据驱动模式(TD驱动),看起来眼熟,其实就是“判定表”,我们以前用Excel写用例的时候,就是这么写的,现在在twork中,更进了一步,用户可以随意定义每个测试集的列,而每一行,也作为一个用例对象,保存在数据库里。如下图:

需要说明的是,这种复杂的组合,程序是无法自动生成用例的,因为要完全排列组合的话,用例太多,不靠谱,而且具体的组合情况,跟需求有很强的关系,程序更是难以了解。程序能做的是,生成用例表格结构,同时创建一些空白的用例,然后我们自己在里面填一下值就可以了,写用例速度快,而且用例非常直观。

大家注意到了,在设计图中,输入、输出、实体我们都用不同的标记给标识出来,这样导入twork时,程序便会自动算出每个逻辑事务的功能点指数分值,非常方便,所以文章开头说,这个指数计算,只是一个副产品。

通过上面的分析过程我们可以看出,功能点分析图与测试用例之间,存在非常紧密的逻辑关系,之前几篇文章我们也讲到,功能点分析是一种非常好的分解分析需求的手段。通过这张分析图,读者可以迅速了解设计者的思路,以及了解每个逻辑事务大致的逻辑。这时如果需要看细节,可以进入twork,很快找到这个逻辑事务的测试集,并查看下面的用例。

上面的例子,列举的是Create类的逻辑事务,以及里面两种最常见的输入组合。Update类和Delete类事务,跟这个差不多,这里不再细讲。它们的共同点在于,输入一般较多,并且存在一些逻辑组合,而输出,相对比较简单。

至于Read类和List类事务,在设计图会有一些区别,这两种逻辑事务输入相对较少,而输出项很多,它们主要的测试重点在于,校验页面展示,比如“查看宝贝信息”,输出项可能会有30个以上,这时,使用check list的方式会比较方便,并不用编写复杂的用例,只需说明,需要校验哪些点即可。如果Read类事务也有复杂的输入,比如查看宝贝信息会有:宝贝类型、宝贝类目、宝贝消保类型这些输入,那么就参考刚才Create类的方式即可。

总之,设计用例重点关注业务逻辑,对于展示类的事务,尽量用简单的方式完成测试设计,至于有些一看到页面,就知道应该怎么测试的事务,即使不写用例,我觉得也问题不大。只要在设计图中把这个事务的输入输出实体都标识清楚,我相信测试工程师就可以很好的完成测试工作。即使交给另一位工程师,只要他也了解这种设计模式,那么也可以测得很好。

功能点算法以及在软件测试中的应用的系列文章,到这里总共编写了4章,我们需要告一段落了。这4章主要讲了功能点算法的概念,以及基本的应用,后面我们会实践一段时间,等积累了足够的经验,我们再继续这个话题,一起期待Part5回归吧。

原文地址:https://www.cnblogs.com/yanghj010/p/5110398.html