跳转至

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

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

精选留言(15)
  • lisa 👍(17) 💬(1)

    微服务本身基于RPC调用,服务拆分的力度比较小,所有从ROI的模型来讲,微服务的代码每次commit以及merge/pull request会进行API测试,这样API测试的运行次数n会加大,分子变大。而相对于单元测试来讲,API测试的建设成本d和维护成本m相对于单元测试会变小很多,导致分母变小。所以在微服务架构下,投资API测试的ROI明显更高,所以就会出现棱形更由于金字塔的情况出现。 但是说实话,我觉得说到底还是模块拆分力度变小带来的变化,拆分的越小,单元测试和API测试的界限就会变得模糊。实际上API测试的运行的成本还是比较高的,他需要初始化数据以及初始化泳道环境,并且稳定性远远低于单元测试,带来的维护成本其实还挺高的。所以我个人还是比较赞成金字塔模型,即便是在微服务的架构下。

    2022-03-26

  • lisa 👍(7) 💬(2)

    最近重新思考了一下这个问题,其实我们没有必要去选择是采用金字塔模型还是菱形,而是更多的是基于测试需求去进行ROI分析,放在哪一层ROI最高就选择在哪一层(可能不同的产品形态会不一样),不用太关注最后会形成什么模型。考虑到目前越往底层的运行成本最低,运行次数最多且最稳定,也就是D和M的时间比较少,大量的测试点会下沉,但是我们同时也要考虑到自动化测试是面向风险,而不是面向代码覆盖率。所以基于风险来看,哪些测试点应该要被设计也存在一个设计评估的过程。最后测试基础设施的完善性以及团队的实际情况也是其中一个重要的考虑因素。总之,最终是一个什么模型是测试点落地的结果,而不是前期的决策依据。

    2022-05-30

  • D_w 👍(6) 💬(1)

    一点浅见: 问题1:在微服务架构下,测试层级会扩充,新增契约测试,组件测试,端到端的测试等等,并且在实际测试任务中一般开发负责的单元测试占比不高,最终成纺锤形。 问题2:如果团队成员能力都比较强,我偏向全栈,如果能力不均衡还是得考虑精细分工。我觉得全栈的测试效果能更好

    2022-03-23

  • lisa 👍(5) 💬(2)

    我希望是最重视全栈的模式,因为对技术的理解成本远远小于对业务的理解成本,也就是一个人把它培训成全栈可能花上一个月就行,但是让这个人充分理解业务以及业务的架构是一件比较长期的事情

    2022-03-26

  • chin 👍(5) 💬(1)

    我们很多人都喜欢引用金字塔模型,但是似乎没有多少人认真思考过金字塔的模型是怎么来的,有无理论依据。这点得向老师学习,拒绝拿来主义

    2022-03-23

  • 萧瑟 👍(2) 💬(1)

    我是这么理解的:在微服务架构的系统中,一个请求进入系统,可能会经过多个服务获取数据,最后整合输出。也就是说一个 UI 测试 会对应多个接口测试,而一个接口测试可能会对应多个不同服务之间的接口调用,单元测试更注重的是单个服务的逻辑、算法。因此接口测试因为运行次数 N 而变得比单元测试的 ROI 更高,测试金字塔也就变成了中间凸出两头小的棒槌形状。

    2022-05-04

  • 一默 👍(1) 💬(1)

    老师请教一下,单元测试无法手动测试,但是因为单元测试应该会到方法级别,所以是不是应该测试用例数也会最多,开发时间和维护时间应该也是比较多的吧。相应的ROI成本也会变低吧。 谢谢老师指教。

    2022-05-27

  • IT蜗壳-Tango 👍(1) 💬(1)

    思考题: 我选择全栈,因为对于特定模块来说,我可以从单元测试,API,UI三个层面去验证这个模块的性能以及可用性。 问题: 目前的自动化测试感觉很少会接触到单元测试层面,尤其是黑合测试的场景。是不是主要将UI, API的ROI考虑的全面一些就可以了啊。

    2022-03-23

  • 随片 👍(0) 💬(1)

    answer: 1、微服务时代,每个系统都是单独的应用,整体性相对是减弱的,独立性提高后,也许该应用在UI层是不体现测试点的。所以金字塔是不符合微服务时代的。 2、要考虑团队规模和项目复杂程度。规模小,那么一人单挑吧,反之,多人负责。项目负责程度与规模类似。一人:优势--编程、维护代码,个人思路更清晰;劣势--时间成本,交接成本,代码模块粘性。多人:时间短,分工明确;劣势--人工成本,测试功能点重叠?

    2022-06-28

  • 志杰 👍(0) 💬(1)

    我目前所参与的微服务,偏向于接口方面,从ROI的模型来讲,接口运行的次数会增加,相对于开发而言,成本会变得小很多;目前项目中没有单元测试,投资接口测试的ROI明显会变高,纺锤型更贴近目前的测试方向。微服务框架下的接口测试,我个人感觉:基于数据,性能,可靠性的要求更贴近目前一个主流趋势。把握当下主流,并剖析测试未来方向,眼界还是不能缺少,学以致用为主,老师所讲的内容让我少走了一些弯路,再此十分感谢;最后,做好一件事情,精力是有限的,对一个事物的理解和打磨需要持续化的时间。

    2022-05-26

  • Geek_d00d65 👍(0) 💬(1)

    是选择全栈还是精细分工,我觉得要依赖于团队成员的能力,如果团队成员一个模块的所有层面测试,效率和时间都比精细分工要好,那我会选择全栈,反之,则会选择精细分工

    2022-04-21

  • 大饼夹一切啊 👍(0) 💬(1)

    1. 由于微服务是按照轻量级的API进行通信的,应在保持UI测试占比不变的情况下,提升接口测试占比,减少单元测试占比,虽然微服务架构降低了单体架构的复杂度,同时也提升了多个服务的维护成本,若将重点放在单元测试,则会大幅增加开发成本、维护成本。 2. 首先表达观点,个人更倾向于精细分工。 优势:(1)对人员技术栈要求相对于全栈低,降低学习成本。 (2)专人专责,避免了个个模块负责人之间的“甩锅”现象。 劣势:(1)由于单人负责所有模块一个层面测试,需要及时对测试代码进行review,并且初期要做好相关的书写规范。(2)单人负责所有模块单层的测试,增加了项目风险,若相关负责人离职,则会对项目交付产生影响。 当然,如果产品、项目生命周期够长的情况下,还是需要进行人员的功能轮换,取长补短,提升测试团队整体能力,以保证产品质量。

    2022-04-03

  • Even 👍(0) 💬(1)

    测试测试金字塔,每一层做自己最擅长的事情,roi综合起来才会最高

    2022-03-27

  • swordman 👍(0) 💬(2)

    说说我这几年的项目经历: 1. Android移动端:全部使用ROI最高的UI自动化测试,开发和维护成本较高,虽然和CI流水线进行了集成,但由于测试用例数量的缺乏,及稳定性不足,并未达到预期的结果; 2. 微信小程序:由于业务的实时性需求,小程序前端承担了大部分的业务和算法处理。因此采用ROI最高的单元测试 + 基本场景的手工UI测试,再加上和代码提交流水线结合,取得了很好的效果; 3. C++多组件项目:正准备着手进行的项目。由于涉及多组件协同工作,拟采用接口测试 + 基本功能UI自动化测试。正愁着如何验证测试策略的正确性呢,老师的课程就来了,给了我一个清晰的指导思路,谢谢!

    2022-03-26

  • 追风筝的人 👍(0) 💬(1)

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

    2022-03-23