You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 19排兵布阵自动化测试的编排、扩展与重构
你好,我是柳胜。
通过前面两讲我们一起推导了一个自动化测试Job元数据模型。接下来我不但会把这个Job模型用到自动化测试设计里还要“点石成金”让你充分体会到这个模型的强大力量。
在讲解设计过程之前,我想先和你聊聊,现在的自动化测试设计有什么问题。
如果你做过自动化测试开发对现有设计一定不陌生。通常做法是创建一个TestSuite它包含多个TestCase每个TestCase完成一个测试任务然后顺序跑下来测试就执行完了。就像下面这样。
![图片](https://static001.geekbang.org/resource/image/c5/5f/c5c1d2c97ea26bb8a1d07724aa5b515f.jpg?wh=1920x962 "传统做法")
我相信,很多自动化测试开发人员刚入行的时候,就是这么学习和组织自动化测试案例的,一直到现在。
但你有没有想过这种测试案例的组织方法是来源于单元测试。它的方法论是有一个开发方法就需要有一个对应的Test方法两者一一对应。因为先有开发的代码存在那么Test方法有多少个都负责干什么事这些都一眼到底几乎不需要测试设计往Test方法里填充代码就可以了。
这种自动化测试开发方法,我叫它**“轻设计,重实现”**的方法。不过,这种方法反过来也会潜移默化地影响自动化测试人员的思维方式。不想好自动化测试的设计,上来就先写代码,结果代码写出来又长又冗余。
而自动化测试已经发展了这么多年早就不再局限于开发阶段的单元测试了有API测试、UI测试等等。这些高层面的自动化测试案例其设计往往以功能为入手点对业务进行分解进行自动化建模。在这个过程中就不能像单元测试那样有现成的开发方法做对标和参考了。
而在这样的背景下,自动化测试案例的设计,逐渐成长为一个非常重要的环节。所以,我们应该要走向“**重设计,轻实现**”的自动化测试开发方法。
所谓**重设计**,其实就是我们要像开发人员一样,去设计自动化测试案例,要考虑后面这三大关键问题:
1.概要设计。要写多少个TestJob它们之间关系是什么
2.详细设计。每个TestJob该怎么实现
3.需要扩展和重构怎么办?
## 概要设计要写多少个Test Job
我们先从概要设计开始梳理。一说概要设计你是不是马上想到的问题就是要写多少个Test
但其实这个问题无法直接解答。因为刚开始规划一个软件系统的自动化测试我们只知道一个大概的场景需求并不会一下子就知道需要开发多少个Test Job。
比如下单功能它是从Web UI下单然后用户通过快递得到食物。为了去测试它需要多少个Test Job做多少个检查点这些问题需要从概要设计中梳理出答案。
那概要设计怎么做?一步步来。
第一步我们先把大概的自动化测试需求梳理出来建模成为测试Job这里我们把测试Job模型再“召唤出来”一一去对号入座。
![图片](https://static001.geekbang.org/resource/image/e6/f7/e6de0d6d56ea556f8584de2353e51ff7.jpg?wh=1920x1310 "测试Job模型")
### 先完成第一个根Job
Job的名字是“UserPlaceOrder”。Job的Input是什么呢要想启动Job需要有一个实例的URL还有登录所需的User Name和Password。
接着往下分析Job的Output是什么呢用户得到食物。那用户得到食物的标准是什么因为物流是Foodcome的第三方服务FoodCome处理完食物的标准是生成物流单号并且用户收到了“食物快递发出”通知短信。那我们UserPlaceOrder的Job Output就是DeliveryTicketNo和DeliveryNotificationMessage两个信息。
Job的TestConfig是什么呢这里我们需要考虑的一个Job参数就是Timeout。
从自动化测试的执行效率角度看每一个Test Job都应该承诺在一定时间内完成这样自动化测试的运行时间才能可控可预期。另外从业务角度每个业务都有[SLA](https://zh.wikipedia.org/wiki/%E6%9C%8D%E5%8A%A1%E7%BA%A7%E5%88%AB%E5%8D%8F%E8%AE%AE)的时间要求。
设想一下我们对客户承诺处理一个订餐的单子最长时间是30分钟那么对应在Test Job的实现中30分钟后如果还没有输出DeliveryTicketNo和DeliveryNotificationMessage这两个信息客户的承诺没有达到Job应该就会失败。所以我们在TestConfig里设定Timeout=30mins。
现在UserPlaceOrder的Job我们就梳理出来了画成六边形是这个样子
![图片](https://static001.geekbang.org/resource/image/99/53/999031947e107324d4cbb617e3bedb53.jpg?wh=1920x728)
它的XML表达是这样一个文档
```xml
<TestJob name="UserPlaceOrder" description="创建订单">
<Input>LoginURL</Input>
<Input>UserName</Input>
<Input>Password</Input>
<Output>DeliveryTicketNo</Output>
<Output>DeliveryNotifyMsg</Output>
<TestConfig>Timeout:30mins</TestConfig>
</TestJob>
```
这个Job写出来后你就完成了UserPlaceOrder这个Job的概要设计也相当于开发完成了一个小型系统的概要设计。
对于开发来说,系统用一个模块就能完成,还是由多个微服务共同完成,这取决于业务复杂程度和实现能力。
那对于自动化测试设计来说这个UserPlaceOrder是直接就可以开发了还是需要继续细化成子Job怎么决策呢我们可以从**复用性**和**实现能力**两个角度,来决定是否拆分。
### 根据复用性进行拆分
先从复用的角度来看登录是所有功能的前置条件如果我们把登录测试任务提炼出来是可以提高ROI的。按照这个思路我们把UserPlaceOrder Job拆分成Login和PlaceOrder两个Job像这样。
![图片](https://static001.geekbang.org/resource/image/e2/3a/e205cc2ff8d14d448a9a5eb40e25d33a.jpg?wh=1920x938)
### 根据实现能力拆分
再琢磨一下PlaceOrder这个Job包含了创建订单和验证订单两块逻辑。创建订单从Web UI上完成而快递单号需要从数据库里查到但是短信要从短信网关上得到所以DeliveryTicketNo和DeliveryNotifMsg是两个技术上独立的实现的方法它们应该被分割成两个Job每个实现用不同的技术这也遵循了**面向对象设计单一职责的SRP原则**。
![图片](https://static001.geekbang.org/resource/image/c1/25/c10b0f0e81ec2fb8da9ebbyy0b4f7f25.jpg?wh=1920x1425)
在分解Job的时候子Job要实现父Job的对外承诺也就是Input和Output。而且在运行的时候一个父Job的所有子Job都太运行通过父Job才能通过。
这就是我们要创建一个VerifyOrder抽象Job的原因。因为它下面的2个子Job即快递单号和通知短信都要成功verifyOrder才算成功。
同样的道理createOrder和verifyOrder都要成功它们的父Job placeOrder才算成功。这样我们的Job设计就咬合在一起了。
讲到这里我们再回顾开头那个问题要写多少个Test Job。是不是就有答案了
作为测试架构师或者自动化测试设计人员你的首要任务是完成抽象Job的设计把Input、Output、Testconfig、Testdata先定义好。之后要继续抽象再做拆分还是按现在定义的Job实现接口这个要交给你的自动化测试团队来决定。你其实不需要过多关心他们是怎么实现的又用了多少个Job。
但是我们要注意, **Job的拆分是有一个度太大了将来维护和诊断就会很费力气**。你想想诊断一个1000行的代码模块和一个100代码模块哪个更容易就能明白了。拆分得太细了就会让复杂度升高。
拆不拆分你要遵循这两大原则第一提高复用性也就是ROI第二方便独立实现和维护。
## 详细设计Test Job怎么实现
可能你注意到了设计Job的过程在思维上是一个笼统的想法逐渐细化的过程反映在Job模型上就是从一个Job生长成一棵Job树的过程。
你可以看看后面这张图,它能形象地描述这个过程。
![图片](https://static001.geekbang.org/resource/image/34/4b/34494bb8f326ca0625c5f5661yyeda4b.jpg?wh=1920x954)
设计完成后这个Job树的每一个叶子就是我们概要设计的成果它是我们最终细化到可开发的实例Job也就是传统的Test Case了。
这些实例Job我们还要决定怎么来实现放在金字塔的哪个层面做测试具体用什么工具框架详细设计做完之后就能进入具体实现环节了。
按照第一模块讲的3KU法则可以回顾[第二讲](https://time.geekbang.org/column/article/497405)我们就可以确定每个Job要在金字塔的哪一层来完成。确定了这一点工具和框架自然也就可以确定了。
对于我们的订餐系统Login放在API层面使用RestAssure框架CreateOrder在API层面使用RestAssure框架DeliveryTicketNoVerify在DB层面使用JDBCDeliveryNotifMsg在SMS gateway层面使用Java或Python。
我在六边形下面加了一个紫色的方块,来代表实现方法。
![图片](https://static001.geekbang.org/resource/image/1e/c4/1e5c03d07cf625f9e5fefa96deaaeac4.jpg?wh=1920x1291)
到这里我们就可以进入到实现阶段了。自动化测试Job就是一个个具体的模块有输入、有输出、有实现方法足以指导自动化测试开发人员去开发代码了。
而负责Job开发的自动化测试开发人员并不需要了解整个Job蓝图只要他按照接口去做就可以把实体Job组装到场景里。这个跟开发的思维是一样的每个开发人员遵循详细设计开发一个个模块最后整个系统就会运转起来也就是**模块化开发**。
## 扩展和重构
设计做得好不好还要考察扩展和重构的能力。所以我们要看一下Job模型是怎么支持扩展和重构的。
### 扩展
假设未来有一天我们发现前面的设计方案有瑕疵比如这个工作流没有走UI层面没有验证UI上的功能表现。那我们就想加一个verifyOrderOnWebUI来验证订单那怎么办呢
我们还是先梳理verifyOrderOnWebUI的Input和Output它所需要的Input用户名、订单号这些信息是否已经存在又是由哪个Job 输出的。明确了这些只需要把verifyOrderOnWebUI的Depdency指向它即可。其实意思就是前面的Job运行完了有了这些信息了verifyOrderWebUI就可以运行。
在这个具体的场景里我们把verifyOrderOnUI就作为verifyOrder下的一个子Job可以复用verifyOrder的Depdency和Input。
![图片](https://static001.geekbang.org/resource/image/e0/82/e07be8a9204f4b27d63cd3a8e7331a82.jpg?wh=1920x1019)
### 重构
跟软件开发一样自动化测试也需要修改、重构。在传统的自动化测试代码里没有TestJob这个概念往往是一个工具下面带着一群TestCase代码。
TestCase之间的依赖和数据交互这些逻辑会存在隐式的耦合一换工具全部的Test Case都要重新写即使重写其中一个Test Case也要倍加小心说不定就会影响到其它的TestCase。
而有了TestJob之后每一个TestJob都有清楚的接口可以有自己独立的实现方法。这也符合面向对象设计中的单一职责SRP原则和开放-封闭OCP原则。我们可以把修改的影响控制在TestJob的范围内。
![图片](https://static001.geekbang.org/resource/image/13/db/13c987dab72847032afcee3aa1460cdb.jpg?wh=1920x1528)
这个修改可以是代码的修改也可以是工具的替换。比如有一天我们发现verifyOrderOnUI的web测试工具Selenium不好用了想替换成Nightwatch怎么办很简单这个替换仅限于这个Job所以用Nightwatch重写一遍只要保证Input和Output不变就是对外承诺不变我们心里就有底了。
## 小结
为了让你理解透彻我再用类比的方式为你概括一下微测试Job理论一个微测试Job就好比开发里的一个微服务它是有独立的接口配置和实现方法。运行这个微Job就是完成了测试任务它会有自己的状态通过、失败还是无法运行同时它会输出自己获得的数据供其他Job使用。
我为你准备了一张图帮你快速回顾微Job模型的设计流程你可以保存下来做个参考。
![图片](https://static001.geekbang.org/resource/image/f1/35/f1273a8e298e6753dddbca1341243735.jpg?wh=1920x1080)
在我看来这个微Job模型是一种新的设计思维。它给我们带来好处是让自动化测试有了**设计先行**的理论基础,而且能够分解复杂的测试场景,降低和工具框架的耦合。
但这也带来了新的问题虽然方法论上站住了但落地的时候会有挑战因为Job会比以前切得更碎Job模型转成自动化测试案例的时候这最后一公里路应该怎么走下一讲我们再继续。
## **思考题**
你的手工测试案例是怎么设计的呢想想能不能也用Job模型来设计呢
欢迎你在留言区跟我交流讨论,也推荐你把这一讲分享给更多朋友。