一
除了纯后台测试或者纯接口测试外,我想大部分人都会接触业务测试,至少我们目前的客户端产品测试就是这样。
之前和我们组客户端测试同学沟通,总是会发现大家用例的关注点大部分都集中在业务逻辑的覆盖上,对具体逻辑的实现,以及底层实现原理的关注偏少。
这样做其实并没有错,用例不就是覆盖需求的么?而需求就是我们说的业务逻辑呀。
但是仔细想一下双 V 模型就会发现,我们缺少了概要设计(集成测试)和详细设计(单元测试)的阶段,直接进入了系统测试,而要求大家在系统测试阶段考虑单元测试和集成测试的点,确实不是每个人都能做到的,事实证明也确实如此。
前段时间看的《软件测试的艺术》刚好有提到三层应用系统的分层:表示层、业务逻辑层和数据访问层,我觉得可以利用这个分层理论,让我们也可以在系统测试阶段考虑到逻辑实现和底层原理的验证。
下面我根据我们当前项目的情况,以我的角度来按这个分层分别进行举例说明。
二
先说说表示层。
我把表示层对应到我们目前的系统测试阶段的需求覆盖上,所有的显性需求的覆盖,都属于表示层,部分基于需求本身的补充和完善的隐性需求也是属于表示层。
设计表示层的用例比较简单,直接和需求说明进行一一对应就行,保证用例覆盖的集合是需求说明集合的超集,那么对显性需求的覆盖率肯定是百分百了。
举个例子。
有个需求中有这么一个描述「该功能的入口是否展示,需要参考注册表 HKCU\Software\test[testvalue] 的值,值为 0 时不展示,值为非 0 时展示。」
针对显性需求的用例覆盖:
验证注册表 HKCU\Software\test[testvalue] 的值为 0 时,功能入口不展示;
验证注册表 HKCU\Software\test[testvalue] 的值为 1 时,功能入口会展示;
验证注册表 HKCU\Software\test[testvalue] 的值为 2 时,功能入口会展示;
验证注册表 HKCU\Software\test[testvalue] 的值为 4294967296 时,功能入口会展示;
针对隐性需求的用例覆盖:
验证注册表 HKCU\Software\test[testvalue] 不存在时,功能入口不展示;
看,我们用例已经覆盖了需求显式说明的所有情况,同时也对部分隐性需求进行了覆盖,如果从表示层角度看,这个覆盖率已经很完全了。
那是否还有可以补充的用例呢?我们继续往下看。
三
再说说业务逻辑层。
所谓的业务逻辑,可以理解为集成测试或者接口测试阶段的测试对象,比如前面那个例子是调用的哪个接口实现的,如果没有调用接口,自己又是如何实现的?
比如经过和开发沟通,我们得到的实现逻辑是这样的「我们内部没有现成的接口,我直接使用的系统提供的 RegGetValue API 来读取的注册表值进行判断的。」
别看就这么一句话,如果从程序实现的角度看,这里面涵盖的信息量可不少。
我们首先想到的,当然去 MSDN 查询 API 使用说明,地址在这里:https://docs.microsoft.com/en-us/windows/desktop/api/winreg/nf-winreg-reggetvaluew
通过文档可以发现,这个 API 的第四个参数 dwFlags 可以指定我们要获取的注册表值的类型,如果类型不匹配,API 直接返回失败,那么这就可以新增一条用例了:
验证注册表 HKCU\Software\test[testvalue] 为 REG_SZ 类型,并且值为 1 时,功能入口会展示;
接着往下看,会发现这个 API 最低支持平台是 Windows Vista,那么新的用例又来了:
验证在 Windows XP 系统上,注册表 HKCU\Software\test[testvalue] 值为 1 时,功能入口会展示;
我们现在回过头来看这两条新增的用例,它们不是显性需求的描述,也不是我们把显性需求进行等价类或边界值进行划分能够补充到的用例,他必须是对具体实现逻辑有了解,才会考虑到的用例。
当然,也有人会说,我不这么分析,我也能想到这些用例,那恭喜你,对实现逻辑的挖掘已经深入你的骨髓,你养成了一个好习惯,千万不要给丢掉了。
四
最后再说说数据访问层。
这里说的数据是广义的,包含数据库存储的数据、注册表里面的数据、具体的文件变化等等,都可以算,概况起来就是,如果需求有涉及到非程序本身的数据变化时,一定要对数据本身进行确认和验证。
这个如果要和标准流程对应的话,部分涉及到单元测试的范畴了,所以如果有开发代码权限的可以多加关注。
如果继续使用前面这个例子来说明的话,那就是相关注册表值的位置、值的类型、值的内容等,这个例子里面这部分内容显性需求里面其实有说明,所以我们换个例子。
比如我们有个流程管理系统,每个流程阶段都需要经过对应角色确认,才能让这个流程继续下去,当初我们定的角色名称有开发、产品和测试,实现的时候,有人就直接把开发、产品和测试这样的字符串写入到数据库了,并把这些字段放到了逻辑判断的语句里面,仅从黑盒测试的角度看,没有任何问题,所有用例都可以通过,但是,如果有一天我们需要把产品改名为产品经理,就会发现要改的地方特别多,测试也几乎需要把所有相关逻辑重跑一遍,并且还避免不了部分日志记录中根本无法分离出这部分数据。
碰到这种情况,我们建议的方式是把一些变化的内容使用不变的代号来表示,比如开发用代号 DEV,那么不管你叫开发、开发经理、开发工程师还是其他的,DEV 的代号是唯一且不变的,那么对逻辑的影响就是微乎其微的了。
同样的,涉及数据库的时候,还需要关注哪些是主键,是否需要建立索引等等(业务测试人员也需要关注这些内容?看你心情了)。
再比如我们有些逻辑涉及到注册表的更新操作,某个操作只需要更新某一个注册表值就行,但是通过过滤规则发现,实现时竟然是把所有相关注册表值都进行了一次重写,如果不去关注数据的变化,仅仅从功能上看是发现不了问题的。
文件操作也是类似,有时候我们仅需要读取部分内容即可,但是实现时是全部读取,甚至是频繁的进行全部读取的操作,造成不必要的 IO 浪费。
五
说了这么多,有没有对前面说的分层有更好的理解呢?有没有可能借助这个理论让我们的用例更深入也更有针对性?
再总结一下:
表示层,就是要关注显性需求以及和显性需求相关联的隐性需求,并设计对应的用例;
逻辑层,就是要关注具体的代码实现逻辑,根据实现补充一些针对性的测试用例;
数据层,就是所有和广义上的数据相关的操作,都要关注实现的合理性和正确性。
以上,希望对你有所帮助,有任何问题欢迎留言沟通。