|
|
# 02|3KU法则:如何找出最优自动化实施截面?
|
|
|
|
|
|
你好,我是柳胜。
|
|
|
|
|
|
上一讲我们提出了自动化测试ROI模型,在回归测试中的应用。回归测试是一个笼统的概念,单元测试、接口测试以及UI测试里都有回归测试,甚至性能测试也已经成为回归测试的一部分。
|
|
|
|
|
|
今天我们要关注一个具体场景,给你一个软件系统,作为自动化测试人员,你怎么找出测试截面,制定自动化测试方案?这些事可能你都做过,觉得并不稀奇,但既然我们已经学习了ROI思维,今天要再加上一个小目标,**制定策略,能够让这个自动化测试设计获得尽可能大的ROI**。换句话说,能干还不够,还要干得好,既要马儿跑,又要马儿少吃草。
|
|
|
|
|
|
有挑战不?那就跟我进入这一讲的学习,一起找到最佳策略吧。
|
|
|
|
|
|
## 测试ROI金字塔
|
|
|
|
|
|
在测试设计领域,经常提到的方法是分层。具体就是给定一个系统,结构上划分三个层级,单元在最小圈;服务包含多个单元,在中圈;而系统又包含多个服务,是外部的最大圈。结构图如下:
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/77/c3/7713a7081ac2723a2cfc35d3277b21c3.jpg?wh=1920x1050 "软件结构圈图")
|
|
|
|
|
|
相应地,我们的测试结构是在代码层做单元测试,服务层做接口测试,系统层做UI功能测试。
|
|
|
|
|
|
在实践中,这三种测试该怎么组合安排呢?迈克·科恩在2009年他的新书《敏捷成功之道》中首次提出了测试金字塔模型。单元测试自动化在金字塔底部,接口测试自动化在中部,而UI测试自动化在金字塔顶部。
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/bd/68/bdyy34691cc7a36c8e50f13e3bbaca68.jpg?wh=1920x1050 "分层测试金字塔")
|
|
|
|
|
|
迈克·科恩讲到自动化测试工作量配比时,认为应该按照层面积分配。也就是说,单元测试案例的数目应该多于接口测试案例数目,接口测试案例数目应该多于UI测试自动化测试案例数目。
|
|
|
|
|
|
后来,金字塔模型又被业界发展,赋予了不同的测试策略,比如自底向上执行速度减慢,自顶向下业务属性减弱,技术属性增强。
|
|
|
|
|
|
但迈克·科恩没有解释,为什么各层工作量配比要按照测试金字塔分布?按照软件结构图,系统在最大圈,测试案例应该最多,而到了自动化测试金字塔,UI自动化测试案例却最少;单元测试在小圈,测试案例应该最少,但到了自动化测试金字塔,单元测试案例却最多。
|
|
|
|
|
|
为什么是金字塔?要是不去理解规律背后这个“为什么”,你就用不好这个规律。上一讲我们知道了“ROI其实是自动化测试的隐式命脉”,现在我们就利用ROI思维,分析一下测试金字塔规律。
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/cd/00/cd34280bc70b3633e696a7ba16f9e300.jpg?wh=1920x868 "ROI公式")
|
|
|
|
|
|
下面,我们分别看看每层的ROI。单元测试可以在开发人员每次code commit触发运行,回归频率高;接口测试在每轮集成测试运行,回归频率中;UI自动化测试在用户验收测试,回归频率低。
|
|
|
|
|
|
按照ROI模型,我们可以得出3种类型自动化测试的ROI排序,如下表:
|
|
|
|
|
|
![](https://static001.geekbang.org/resource/image/71/08/713a743c26347d08d561d5a77ab27608.jpg?wh=3363x1379)
|
|
|
|
|
|
对照测试金字塔不难发现,实际上三类自动化测试的ROI是自底向上由高到低的。
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/7e/af/7ebe91f53e1fc7a84e53a26d68c4baaf.jpg?wh=1920x1050 "分层测试ROI金字塔")
|
|
|
|
|
|
按照第一讲得出的规律“自动化测试顺序从ROI高到低”,我们优先投入精力做ROI最高的单元测试,再做ROI中的接口测试,最后完成UI测试。
|
|
|
|
|
|
现在就可以轻松解释迈克·科恩的金字塔了,因为ROI存在差异,所以按照高ROI大投入,中ROI中投入,低ROI小投入,工作量比例呈金字塔分布,底层面积最大,顶层面积最小。发现没?**根源在于ROI,金字塔是表现出来的形态而已**。
|
|
|
|
|
|
好,到这里,总结一下。各种软件理论学派,大致可以分为两种,一种是理论基础,讲的是做什么,比如软件测试定义、软件过程,另外一种是实践经验,讲的是该怎么做,比如金字塔模型。
|
|
|
|
|
|
实践和理论很大的不同就是在现实商业中,我们不可能完全按照理想来工作,而是要加入很多制约因素,其中最大的制约就是钱。明白这个道理,你就会知道为什么ROI是根源,你也会知道怎么能够在工作中做出业绩了,不是耍两个工具,忽悠一下领导就算成功,而是认认真真地去思考,踏踏实实地去提高ROI,直到边际效应ROI无法提高为止。
|
|
|
|
|
|
## 寻找最优ROI策略
|
|
|
|
|
|
刚才说了分层测试和各层ROI,业界也很认可这种分层理论,但实际落地时却存在问题:一批人做UI测试自动化,另外一批人去做接口测试,然后开发人员做单元测试。三路人马忙得不亦乐乎,都说自己贡献大,等到bug发生了泄漏到生产环境,又开始甩锅。
|
|
|
|
|
|
### 分层测试为啥会“内卷”
|
|
|
|
|
|
很明显,这是一个内卷的场景,让我们结合例子具体看看内卷发生在哪里?
|
|
|
|
|
|
以一个Web登录操作为例,用户在UI上输入用户名和密码,点击“登录”按钮。Selenium UI 自动化会这样实现:
|
|
|
|
|
|
```plain
|
|
|
@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网关,是这样的:
|
|
|
|
|
|
```plain
|
|
|
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'
|
|
|
|
|
|
```
|
|
|
|
|
|
在单元上执行的则是这样的代码:
|
|
|
|
|
|
```plain
|
|
|
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测试和单元测试三个测试截面。
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/0a/ce/0a1af39ca343083b14fe1472c5610cce.jpg?wh=1920x1045 "三个测试截面示意图")
|
|
|
|
|
|
三个测试截面测的是一个请求在不同层面上的形态,那么每一个截面都可以测试全部的案例,也可以测试部分的案例。就像3个人负责1个项目一样,如果没有经过事先的协调和安排,3个人可能做了重复的事情,造成浪费,也可能存在一件事3个人都没干,形成测试盲区。
|
|
|
|
|
|
### 需求/策略矩阵
|
|
|
|
|
|
这种“内卷”是不是一个问题?可能你会说没问题,各层独立测试能够加强质量保障。说这话的底气在于测试上的投入充足,不计内卷成本。实际上,在DevOps风行的今天,趋势是追求效果和效率。所以,在资源有限的条件下,我们需要在整体上看待分层测试的最优ROI。
|
|
|
|
|
|
咱们先看看测试需求是什么,用 [FURPS模型](https://zh.wikipedia.org/wiki/FURPS)来理一下需求。FURPS是用5个维度来描述一个软件的功能需求,FURPS这个单词对应着每个需求的英文首字母:
|
|
|
|
|
|
* F=Function 功能
|
|
|
* U=Usability 易用性
|
|
|
* R=Reliability 可靠性
|
|
|
* P=Performance 性能
|
|
|
* S=Supportability 可支持性
|
|
|
|
|
|
把测试需求和测试类型组合在一起,就整合了后面这个矩阵表格:
|
|
|
|
|
|
![](https://static001.geekbang.org/resource/image/44/21/443b6845a599a19d27704a3f89b44b21.jpg?wh=4000x1410 "3KU测试矩阵")
|
|
|
|
|
|
结合表格,可以看到UI测试、接口测试和单元测试每个截面的测试能力。
|
|
|
|
|
|
* 在UI层面上,功能性最强,所有测试需求都可以做。这个可以理解,因为软件本身就是满足用户需求,没有一个需求不可以从用户层面感受到。如果真的存在一个需求,用户却无法体验到,那根据[奥卡姆剃须刀原理](https://zh.wikipedia.org/wiki/%E5%A5%A5%E5%8D%A1%E5%A7%86%E5%89%83%E5%88%80),这种用户无法体验到的需求就是无效的。
|
|
|
* 接口层面上,功能性减弱,技术性增强。
|
|
|
* 单元层面上,技术性最强,功能性主要体现在数据的处理,算法逻辑上。
|
|
|
|
|
|
### 3KU整体策略
|
|
|
|
|
|
好,有了需求/策略矩阵后,结合上面讲到的自动化测试ROI金字塔,我们的整体最优ROI策略就呼之欲出了。什么是整体最优ROI呢?
|
|
|
|
|
|
有3个Key(关键因素):
|
|
|
|
|
|
* **U**seful: 每个测试需求都是有效的;
|
|
|
* **U**ltimate: 每个测试需求的验证都在优先寻找自动化ROI高的层面去实现,如果不可行,按照ROI高到低回退,直到UI层;
|
|
|
* **U**nique: 每个层面上验证的测试需求都和别的层面都不是重复的。
|
|
|
这样分配的工作,既不重复,又没遗漏,还遵循了ROI的原则。我管它叫**3KU原则。**
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/7e/af/7ebe91f53e1fc7a84e53a26d68c4baaf.jpg?wh=1920x1050 "3KU测试金字塔")
|
|
|
|
|
|
3KU策略该怎么执行呢?按照3KU策略,我们把表格里的测试需求,对照下面这三个问题,按顺序检查一遍:
|
|
|
|
|
|
1.能在单元测试验证么?
|
|
|
2.能在接口测试验证么?
|
|
|
3.能在UI测试验证么?
|
|
|
|
|
|
这样检查以后,就能得出各个需求的自动化实现截面了。
|
|
|
|
|
|
UI测试关注功能场景测试,易用性测试和可执行性测试;而接口测试关注不同数据的循环,接口的性能和错误恢复能力;单元测试关注算法的正确性和性能。
|
|
|
|
|
|
恭喜你看到这里,最后就是我们收割成果的环节了。我们又得出了一个满足3KU原则的自动化测试实施金字塔,各层有自己的关注点,又在整体上实现了互相配合补偿。
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/95/a7/950bdb1892c70d0598cd0657e2ca92a7.jpg?wh=1920x1064 "3KU测试金字塔")
|
|
|
|
|
|
在3KU测试金字塔下,每一个测试需求都会选择最大的ROI测试截面,通过这样的安排,实现了整体最优ROI的目标。对不对?
|
|
|
|
|
|
## 小结
|
|
|
|
|
|
这一讲,我们从ROI角度分析了一下分层测试的原理和在实践中的应用。先入为主地,分层理论上的分层测试的特性,必然会造成重叠和错失。这给测试从业者带来了挑战。但挑战也是机会,如何解决这个问题?
|
|
|
|
|
|
这就需要我们遵循回归到效益的原则,思考怎么用最少的资源干最多的事,能达到这个效果,就是好的实践。因此,我们提出了分层但协调实现整体最优ROI的解决方案,3KU测试矩阵和3KU测试金字塔。
|
|
|
|
|
|
![](https://static001.geekbang.org/resource/image/44/21/443b6845a599a19d27704a3f89b44b21.jpg?wh=4000x1410 "3KU测试矩阵")
|
|
|
|
|
|
![图片](https://static001.geekbang.org/resource/image/95/a7/950bdb1892c70d0598cd0657e2ca92a7.jpg?wh=1920x1064 "3KU测试金字塔")
|
|
|
|
|
|
沿着这个思路,各层做好自己具有优势能力的测试需求,比起全部需求系于端到端的测试上,更有效率和效益,**分层是追求整体ROI的结果**。之后的课程里我们还会反复提到ROI,最后你也会不由感叹,ROI是背后无形的大手,大道无形,无处不在。
|
|
|
|
|
|
## 思考题
|
|
|
|
|
|
1 软件大师马丁·福勒曾经说过:“在微服务时代,分层测试不再呈现金字塔形状。”这是为什么?试着用ROI来解释一下。
|
|
|
|
|
|
2 学完今天的内容,如果你是测试主管,你希望你的团队是全栈(一个人负责一个模块的所有层面测试),还是精细分工(一个人负责所有模块的一个层面测试)?有什么优劣?
|
|
|
|
|
|
欢迎你在留言区跟我交流互动,如果这一讲对你有启发,也推荐你分享给身边更多同事和朋友。
|
|
|
|