大师兄

02|3KU法则:如何找出最优自动化实施截面?

你好,我是柳胜。

上一讲我们提出了自动化测试ROI模型,在回归测试中的应用。回归测试是一个笼统的概念,单元测试、接口测试以及UI测试里都有回归测试,甚至性能测试也已经成为回归测试的一部分。

今天我们要关注一个具体场景,给你一个软件系统,作为自动化测试人员,你怎么找出测试截面,制定自动化测试方案?这些事可能你都做过,觉得并不稀奇,但既然我们已经学习了ROI思维,今天要再加上一个小目标,制定策略,能够让这个自动化测试设计获得尽可能大的ROI。换句话说,能干还不够,还要干得好,既要马儿跑,又要马儿少吃草。

有挑战不?那就跟我进入这一讲的学习,一起找到最佳策略吧。

测试ROI金字塔

在测试设计领域,经常提到的方法是分层。具体就是给定一个系统,结构上划分三个层级,单元在最小圈;服务包含多个单元,在中圈;而系统又包含多个服务,是外部的最大圈。结构图如下:

图片

相应地,我们的测试结构是在代码层做单元测试,服务层做接口测试,系统层做UI功能测试。

在实践中,这三种测试该怎么组合安排呢?迈克·科恩在2009年他的新书《敏捷成功之道》中首次提出了测试金字塔模型。单元测试自动化在金字塔底部,接口测试自动化在中部,而UI测试自动化在金字塔顶部。

图片

迈克·科恩讲到自动化测试工作量配比时,认为应该按照层面积分配。也就是说,单元测试案例的数目应该多于接口测试案例数目,接口测试案例数目应该多于UI测试自动化测试案例数目。

后来,金字塔模型又被业界发展,赋予了不同的测试策略,比如自底向上执行速度减慢,自顶向下业务属性减弱,技术属性增强。

但迈克·科恩没有解释,为什么各层工作量配比要按照测试金字塔分布?按照软件结构图,系统在最大圈,测试案例应该最多,而到了自动化测试金字塔,UI自动化测试案例却最少;单元测试在小圈,测试案例应该最少,但到了自动化测试金字塔,单元测试案例却最多。

为什么是金字塔?要是不去理解规律背后这个“为什么”,你就用不好这个规律。上一讲我们知道了“ROI其实是自动化测试的隐式命脉”,现在我们就利用ROI思维,分析一下测试金字塔规律。

图片

下面,我们分别看看每层的ROI。单元测试可以在开发人员每次code commit触发运行,回归频率高;接口测试在每轮集成测试运行,回归频率中;UI自动化测试在用户验收测试,回归频率低。

按照ROI模型,我们可以得出3种类型自动化测试的ROI排序,如下表:

对照测试金字塔不难发现,实际上三类自动化测试的ROI是自底向上由高到低的。

图片

按照第一讲得出的规律“自动化测试顺序从ROI高到低”,我们优先投入精力做ROI最高的单元测试,再做ROI中的接口测试,最后完成UI测试。

现在就可以轻松解释迈克·科恩的金字塔了,因为ROI存在差异,所以按照高ROI大投入,中ROI中投入,低ROI小投入,工作量比例呈金字塔分布,底层面积最大,顶层面积最小。发现没?根源在于ROI,金字塔是表现出来的形态而已

好,到这里,总结一下。各种软件理论学派,大致可以分为两种,一种是理论基础,讲的是做什么,比如软件测试定义、软件过程,另外一种是实践经验,讲的是该怎么做,比如金字塔模型。

实践和理论很大的不同就是在现实商业中,我们不可能完全按照理想来工作,而是要加入很多制约因素,其中最大的制约就是钱。明白这个道理,你就会知道为什么ROI是根源,你也会知道怎么能够在工作中做出业绩了,不是耍两个工具,忽悠一下领导就算成功,而是认认真真地去思考,踏踏实实地去提高ROI,直到边际效应ROI无法提高为止。

寻找最优ROI策略

刚才说了分层测试和各层ROI,业界也很认可这种分层理论,但实际落地时却存在问题:一批人做UI测试自动化,另外一批人去做接口测试,然后开发人员做单元测试。三路人马忙得不亦乐乎,都说自己贡献大,等到bug发生了泄漏到生产环境,又开始甩锅。

分层测试为啥会“内卷”

很明显,这是一个内卷的场景,让我们结合例子具体看看内卷发生在哪里?

以一个Web登录操作为例,用户在UI上输入用户名和密码,点击“登录”按钮。Selenium UI 自动化会这样实现:

@Test
public void login() {
WebDriver driver=new ChromeDriver();
driver.manage().window().maximize();
//打开页面
driver.get("https://www.example.com/users/sign_in");
WebElement username=driver.findElement(By.id("user_email_Login"));
WebElement password=driver.findElement(By.id("user_password"));
WebElement login=driver.findElement(By.name("login"));
//输入用户名
username.sendKeys("liusheng@example.com");
//输入密码
password.sendKeys("123456");
//点击登录按钮
login.click();
}

上面UI的操作被Web服务转化成Rest请求,进入到API网关,是这样的:

curl --location --request POST 'http://auth.example.com/auth/realms/Test/users' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'username=liusheng@example.com' \
--header 'password=123456'

在单元上执行的则是这样的代码:

public Future<ResponseData> login(String userName, String password) {
//入口参数检验
if (StringUtil.isBlank(userName)||StringUtil.isBlank(password)){
return new AsyncResult<>(ResponseData.error("账号密码不能为空"));
}
//查询用户是否存在
List<User> userList = baseMapper.getUserInfo(userName);
if (CollectionUtils.isEmpty(userList)){
return new AsyncResult<>(ResponseData.error("账号不存在"));
}
//验证账号密码是否正确
User user = userList.get(0);
String requestMd5 = SaltUtil.md5Encrypt(password, user.getSalt());
String dbMd5 = user.getPassword();
if (dbMd5 == null || !dbMd5.equalsIgnoreCase(requestMd5)) {
return new AsyncResult<>(ResponseData.error("账户密码不正确"));
}
//生成 access token,并返回
String token = JwtTokenUtil.generateToken(user);
return new (ResponseData.success(token));
}

可以看到,一个请求,从浏览器页面发起,进入API网关,再传递到服务里的Login函数,经过了UI测试、API测试和单元测试三个测试截面。

图片

三个测试截面测的是一个请求在不同层面上的形态,那么每一个截面都可以测试全部的案例,也可以测试部分的案例。就像3个人负责1个项目一样,如果没有经过事先的协调和安排,3个人可能做了重复的事情,造成浪费,也可能存在一件事3个人都没干,形成测试盲区。

需求/策略矩阵

这种“内卷”是不是一个问题?可能你会说没问题,各层独立测试能够加强质量保障。说这话的底气在于测试上的投入充足,不计内卷成本。实际上,在DevOps风行的今天,趋势是追求效果和效率。所以,在资源有限的条件下,我们需要在整体上看待分层测试的最优ROI。

咱们先看看测试需求是什么,用 FURPS模型来理一下需求。FURPS是用5个维度来描述一个软件的功能需求,FURPS这个单词对应着每个需求的英文首字母:

  • F=Function 功能
  • U=Usability 易用性
  • R=Reliability 可靠性
  • P=Performance 性能
  • S=Supportability 可支持性

把测试需求和测试类型组合在一起,就整合了后面这个矩阵表格:

结合表格,可以看到UI测试、接口测试和单元测试每个截面的测试能力。

  • 在UI层面上,功能性最强,所有测试需求都可以做。这个可以理解,因为软件本身就是满足用户需求,没有一个需求不可以从用户层面感受到。如果真的存在一个需求,用户却无法体验到,那根据奥卡姆剃须刀原理,这种用户无法体验到的需求就是无效的。
  • 接口层面上,功能性减弱,技术性增强。
  • 单元层面上,技术性最强,功能性主要体现在数据的处理,算法逻辑上。

3KU整体策略

好,有了需求/策略矩阵后,结合上面讲到的自动化测试ROI金字塔,我们的整体最优ROI策略就呼之欲出了。什么是整体最优ROI呢?

有3个Key(关键因素):

  • Useful: 每个测试需求都是有效的;
  • Ultimate: 每个测试需求的验证都在优先寻找自动化ROI高的层面去实现,如果不可行,按照ROI高到低回退,直到UI层;
  • Unique: 每个层面上验证的测试需求都和别的层面都不是重复的。
    这样分配的工作,既不重复,又没遗漏,还遵循了ROI的原则。我管它叫3KU原则。

图片

3KU策略该怎么执行呢?按照3KU策略,我们把表格里的测试需求,对照下面这三个问题,按顺序检查一遍:

1.能在单元测试验证么?
2.能在接口测试验证么?
3.能在UI测试验证么?

这样检查以后,就能得出各个需求的自动化实现截面了。

UI测试关注功能场景测试,易用性测试和可执行性测试;而接口测试关注不同数据的循环,接口的性能和错误恢复能力;单元测试关注算法的正确性和性能。

恭喜你看到这里,最后就是我们收割成果的环节了。我们又得出了一个满足3KU原则的自动化测试实施金字塔,各层有自己的关注点,又在整体上实现了互相配合补偿。

图片

在3KU测试金字塔下,每一个测试需求都会选择最大的ROI测试截面,通过这样的安排,实现了整体最优ROI的目标。对不对?

小结

这一讲,我们从ROI角度分析了一下分层测试的原理和在实践中的应用。先入为主地,分层理论上的分层测试的特性,必然会造成重叠和错失。这给测试从业者带来了挑战。但挑战也是机会,如何解决这个问题?

这就需要我们遵循回归到效益的原则,思考怎么用最少的资源干最多的事,能达到这个效果,就是好的实践。因此,我们提出了分层但协调实现整体最优ROI的解决方案,3KU测试矩阵和3KU测试金字塔。

图片

沿着这个思路,各层做好自己具有优势能力的测试需求,比起全部需求系于端到端的测试上,更有效率和效益,分层是追求整体ROI的结果。之后的课程里我们还会反复提到ROI,最后你也会不由感叹,ROI是背后无形的大手,大道无形,无处不在。

思考题

1 软件大师马丁·福勒曾经说过:“在微服务时代,分层测试不再呈现金字塔形状。”这是为什么?试着用ROI来解释一下。

2 学完今天的内容,如果你是测试主管,你希望你的团队是全栈(一个人负责一个模块的所有层面测试),还是精细分工(一个人负责所有模块的一个层面测试)?有什么优劣?

欢迎你在留言区跟我交流互动,如果这一讲对你有启发,也推荐你分享给身边更多同事和朋友。