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.

304 lines
21 KiB
Markdown

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden 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.

# 24启动运行基于Job模型的框架实现和运行
你好,我是柳胜。
今天是设计篇的最后一讲从第十七讲到现在我们围绕Job元数据模型讲了很多内容。毕竟这个Job模型设计是一个新的设计方法论需要从模型推演、设计方法、不同场景的设计案例等各个方面来学习理解。虽然过程中有点像盲人摸象一样但我们最后终于勾勒出了一个全面的理解。
不过一种新的设计方法想要生根发芽不光要在理论层面讲得通还要让使用者轻松快速地掌握它并且学以致用。沿着这个思路今天我们把基于Job模型的框架实现梳理一遍明确都需要实现什么以及怎么实现。
经历了这个思考过程对你把Job模型在团队里推广和使用大有帮助而且对你去设计、推广自己的框架也同样适用。一个设计方法要在工作里落地它就必须要和日常的设计活动开发维护活动结合在一起有3个关键场景
1.自动化测试设计的评审和交流(设计文档化);
2.自动化测试设计和实现的一致性维护(设计代码化);
3.自动化测试的报告呈现(结果可视化)。
## Job设计文档化
我们先从设计评审和交流说起这离不开一份清晰易读的设计文档。在软件技术迅猛发展的今天开发人员可以用Swagger来表达API的接口设计运维人员可以用YAML来表达部署的方案但**测试人员却没有一个表达自动化设计的文档格式,真是遗憾**。
而现在有了Job模型后作为自动化测试人员你可以骄傲地说我的自动化测试也有设计文档
文档化怎么做呢在前面几讲的案例中我们都是用画六边形的方法来表达Job树的设计。这种方法有点像画思维导图一边想一边展开帮助你迅速完成设计。但设计是最终要输出成文档的尤其我们要版本化管理设计文档的时候。
这个设计文档用什么格式比较好呢我们需要把Job树图转成可读的数据文件Job树是一个树形结构那我们继续思考树形结构适合用什么格式来表达业界里的树形结构数据表达格式已经非常多了XML、Json、YAML都能胜任。
![图片](https://static001.geekbang.org/resource/image/bc/de/bcdf661a2f885ab48550e2ac19dabdde.jpg?wh=1920x627)
用XML作为载体展现视频会议视频会议案例可以回顾[第二十三讲](https://time.geekbang.org/column/article/517078)的Job设计树也就是TestJobFile.xml的例子如下
```xml
<TestJob name="VideoMeeting" description="Meeting Full automation">
<TestJob name="ScheduleMeeting">
<TestJob name="createMeeting" host="10.0.0.1" driver="Selenium">
<JobOutput name="MEETING_URL"/>
</TestJob>
<TestJob name="JoinMeetingDesktop" host="10.0.0.2" depends="createMeeting" driver="WinappDriver">
<JobInput name="MEETING_URL"/>
</TestJob>
<TestJob name="JoinMeetingAnroid" host="10.0.0.3" depends="createMeeting" driver="appium">
<JobInput name="MEETING_URL"/>
</TestJob>
</TestJob>
<TestJob name="VerifyJoin" depends="ScheduleMeeting" driver="Restassure">
</TestJob>
<TestJob name="PresentMeeting">
<TestJob name="presentPPT" host="10.0.0.1" driver="Selenium">
<JobOutput name="TR_START_TimeBetweenPresentAndView"/>
</TestJob>
<TestJob name="viewPPT" host="10.0.0.2" driver="WinappDriver">
<JobOutput name="TR_END_TimeBetweenPresentAndView"/>
</TestJob>
<TestJob name="viewPPT" hot="10.0.0.3" driver="appium">
<JobOutput name="TR_END_TimeBetweenPresentAndView"/>
</TestJob>
</TestJob>
```
当然你用YAML和Json也可以达到同样的表达效果。
有了设计文档后自动化测试设计就有了自己的表达方式。这就像你和别人聊天的时候都在说流行的名词“DevOps”但有人认为DevOps是CICD有人认为Devops是微服务架构你甚至无法确定你们讨论的是同一件事。
这个时候怎么办你需要定义一个内容的骨架从三个方面人员组织、流程、技术栈来谈论DevOps。这样团队的表达方式统一了交流就会高效了。
所以,能让一份设计文档被不同的人理解,还需要有一致的格式规范。这个格式规范的目的是**让所有人都用同样的关键词**比如Input、Output、TestData等都遵守同样的约束比如同级依赖实例对抽象的实现等等。
怎么来验证这些格式规范? 你可能已经想到了XML和YAML用schema验证Json用JsonValidator来验证。
下面我们结合例子来看看如何用一个TestJobSchema.xsd文件来验证TestJobFile.xml是否遵循了关键词使用规范。
```xml
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com/jobSchema"
xmlns="http://www.example.com/jobSchema"
elementFormDefault="qualified">
<xsd:element name="TestJob">
<xsd:complexType>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="TestJob" type="TestJobType" minOccurs="1" maxOccurs="unbounded"/>
<xsd:group ref="TestJobGeneralElements"/>
<xsd:group ref="TestJobElementsGroup"/>
</xsd:choice>
<xsd:attributeGroup ref="TestJobGeneralAttributes"/>
</xsd:complexType>
</xsd:element>
<xsd:attributeGroup name="TestJobGeneralAttributes">
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="description" type="xsd:string" use="required"/>
<xsd:attribute name="depends" type="xsd:string" use="optional"/>
<xsd:attribute name="host" type="xsd:string" use="optional"/>
<xsd:attribute name="timeout" type="xsd:nonNegativeInteger" use="optional"/>
</xsd:attributeGroup>
<xsd:group name="TestJobGeneralElements">
<xsd:sequence>
<xsd:element name="JobInput" type="JobInputType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="JobOutput" type="JobOutputType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="TestData" type="TestDataType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:group>
</xsd:schema>
```
有了Schema文件后就能保证不同的人设计出来的Job都遵循同样的规范。然后就可以在团队里开展设计评审、版本升级维护等等自动化测试设计相关的活动了。这样自动化测试设计就在团队激活了
## Job设计代码化
按照软件生命周期,设计完成后,工作重心就需要转移到开发阶段了。传统开发模式里,这个过程一般是手工的,在设计阶段设计好多少个模块,每个模块的出入口是什么,再交由开发人员根据设计文档来实现代码。
这种模式在软件迭代初期是奏效的因为设计和代码是一致的。但随着时间推移迭代次数增加不断修改bug团队成员也换了几波后代码和设计就会产生越来越大的偏差这就是我们常说的“**架构腐化**”问题。
自动化测试开发本质上也是软件开发活动,当然也存在设计和代码脱节的风险。如果不解决掉这个问题,自动化测试设计最后还是会成为形式工程。试想一下,设计做了,文档也有了,但代码就是不按照设计来做,长此以往,谁还会注重自动化测试设计呢?
怎么办?解决架构腐化是个棘手的问题,幸运的是,现在业界也认识到了这一点,推出了很多方法论和技术。其中一个方法,就是通过设计直接生成代码来保持两者的一致性。
我在[第十六讲](https://time.geekbang.org/column/article/511982)里讲过Cucumber框架利用Cucumber测试设计的Feature文件可以直接转化成Java代码Feature文件里的关键字GivenWhenThen以注解的方式加在函数声明上。这就是设计转成代码的方法之一。
那TestJob该怎么做才能让设计和代码保持一致呢
### Auto Gen Auto注解驱动机制
首先我们可以采用Cucumber的思路把Job模型的关键字与代码概念做一个映射。比如Java代码Job结构和Class结构有以下的对应关系。
![图片](https://static001.geekbang.org/resource/image/3e/34/3eff1a2d4e40036c870f62675e2d6534.jpg?wh=1920x947)
然后,再用上[第五讲](https://time.geekbang.org/column/article/499339)里Auto Gen Auto提到的思路把模版自动化生成测试代码的方法用到这里。
如下图所示由Job Parser来解析Job树然后将解析后的数据注入到代码模版里就可以实现Job设计树到代码的自动转换了。
![图片](https://static001.geekbang.org/resource/image/75/78/75741ef381eeec4a7a0e12ca11a74178.jpg?wh=1920x969)
模版技术你可以使用Mustache链接在[这里](https://en.wikipedia.org/wiki/Mustache_(template_system)))。
大致过程是这样的。首先我们定义好后缀名为TestJob.mustache的代码模版文件如下所示
```plain
package {{package}};
import java.util.*;
@dependency({{dependency}})
public class {{classname}} exetnds TestJob{
{{#Inputs}}
@Input
public String {{input}};
{{/inputs}}
{{#Outputs}}
@Output
public String {{output}};
{{/Outputs}}
public run(){
//TODO
}
}
```
然后通过Job Parser解析Job文件向Mustache模版注入变量值就可以生成代码了! 你可以脑补一下生成的代码。
这种Auto Gen Auto方式解决了从Job设计到自动化测试代码的转换过程自动化测试开发人员只需填充测试逻辑的实现代码即可。
Job怎么跑呢这个时候JobRunner需要解析上面代码里的@Dependency、@Input、@Output的一个个注解进行计算得出执行路径然后在运行时把数据注入到框架里。整个运行过程是由注解驱动的。
其实业界流行的开发框架Spring Boot就是这种注解驱动的机制你可以作为参考。
![图片](https://static001.geekbang.org/resource/image/63/38/635fa66373a33f7941e2a8867928bc38.jpg?wh=1920x682)
这个机制的优点是强大扩展性强但另外一面是对Job Parser和Job Runner的开发技术要求高。再说直白点就是这样的自动化测试框架开发投产比会比较低只有稳定的测试组织才能负担得起这样的开发投入和回报周期。
另外还有一种实现思路更轻量Job设计文件就是可运行的文件我们来看看。
### 设计文件即运行文件
如果你用过Apache Ant这个构建管理工具的话你一定对编写Build.xml不会陌生。编写build.xml就是做任务设计先创建Compile、Package、Test的Task声明它们的调用关系然后运行下面的命令
```plain
ant -f build.xml
```
运行命令后build.xml里定义的task就启动运行了。
对于TestJobFile.xml我们也可以采用相同的思路来处理。如果想让TestJobFile.xml里的TestJob能够启动运行就需要把实例Job的运行细节填充进去。
比如一个通过QTP工具实现的实体Job它要包含JobOutput、TestData数据源的类型和位置路径还有引用的Library文件和运行的测试脚本路径。像下面这样
```xml
<QTP  name="QTP_ScriptMode" description="QTP Script mdoe" depends="Java_Init">
  <JobOutput name="TicketNo"/>
<TestData type="xls" location="testscripts\QTP\UILabelData\data_UI_Flight.xls"/>"
<Lib location="common\lib_app\lib_flightdemo_login.vbs"/>
  <Lib location="common\lib_app\lib_utility.vbs"/>
  <Run path="testscripts\QTP\QTP_ScriptMode">
</QTP>
```
而一个Selenium实现的实体Job要包含Testdata还有Java的ClassPath、TestCaseClass和TestMethod。
```xml
<Selenium name="selenium_demo" description="Test calc"  depends="" >
    <TestData type="xml"  location="selenium\config.xml"/>
    <JobInput name="$number1"/>
    <ClassPath location="selenium\selenium-java-client-driver_self_extended_oracle.jar"/>
    <ClassPath location="selenium\my.jar"/>
<ClassPath location=" selenium\selJava.jar"/>
    <SelTestCase path="selJava">
        <SelTest name="testSelJava "/>
     </SelTestCase>
</Selenum>
```
通过代码我们可以看到每种工具的实体Job都有一个Job定义的格式那么我们每完成一个Job的开发就把这个Job的运行信息更新到TestJobFile.xml里去。
像Ant运行build.xml, Maven运行pom.xml一样用JobRunner直接运行TestJobFile.xml, 运行命令如下,这样就能启动自动化测试执行了。
```plain
JobRunner -f TestJobFile.xml
```
这样做主要有两个好处。第一设计文件就是运行文件。Job开发完需要更新设计文件直接就可以运行不存在设计和开发脱节的问题第二每个实体Job的运行都由JobRunner来驱动。这样一来JobRunner就能判断出Job有没有遵循它在设计文件里的承诺的Output一旦Job的实现有了偏差立刻就能发现。
我把TestJobFile.xml样例更新到了专栏的GitHub里你可以点击[这里](https://github.com/sheng-geek-zhuanlan/autmation-arch/tree/master/module3)查看。
当然这个思路也有缺点当TestJob定义过多的时候这个TestJobFile.xml就会过于臃肿难以维护。不过这个时候也有解决办法我们可以让TestJobFile支持引用和嵌套就像build.xml和Maven里的pom.xml一样。
## Job运行报告
文档化和代码化的问题说完了我们再来聊聊测试报告。测试报告是自动化测试最终的结果呈现。怎样利用Job模型来优化测试报告让报告更好地服务各方人员呢
我们先看一下传统的自动化测试报告是什么样的它们包含什么信息又是怎么展现的。Junit框架产生的报告是这样的
![图片](https://static001.geekbang.org/resource/image/58/97/584b5bee8ec157891d24d0c256f81a97.jpg?wh=1920x1228)
发现了么它报告的结构跟TestCase结构是一样的。在设计时一个TestCase下有多个Test运行产生报告后结构保持不变。单个Test变成详细报告有结果状态信息、检查点还有Log输出而TestCase变成汇总报告聚合Test的执行结果有成功率、失败率和执行时间等等。
再来看一下Jira产生的测试报告
![图片](https://static001.geekbang.org/resource/image/12/bb/1287fbb5266dfe852b5b0d115ff951bb.jpg?wh=1920x1113)
相比Junit报告Jira的测试报告又丰富了一些信息。多了一个执行历史的维度能看到一个TestCase执行的历史记录还有TestCase和需求的关联关系从这里就能得出测试对需求的覆盖度。
看了Junit和Jira的测试报告后你会发现传统的测试报告里的数据定义不一样展现风格也不一样。为什么会有这么大差别呢
测试报告形式多样,但不管测试报告有多少种,有一个基本常识是不变的:**测试报告是给人看的**。只不过,不同角色的人,希望从测试报告里得到的信息是不一样的。
测试管理者想要确定发布的信心在一个发布周期内测试对需求的覆盖度关键Bug的数量。
测试经理关注测试设计的质量和测试执行的效率比如测试案例发现Bug的数量、自动化测试执行的时间和假失败的次数等等。
自动化测试开发人员想要快速诊断错误自然关注点就放在了出错的Log、抓图还有出错时的现场信息等等。而从测试报告里开发人员也希望得到错误定位到开发模块的信息比如出错时间、服务端的执行链条、服务端log等等。
你能看到不同角色的关注点不同而且这些关注点随着项目的进展也会发生变化。而我们通过Jira和Junit的例子看到传统的测试报告数据和格式都很固定。
那么Job模型能不能满足这些需求呢既能产生多样数据的报告又能给不同的角色看还能够灵活扩展。
听起来有点复杂和烧脑,不过,如果把这个问题当作一个数据工程来处理,就能把复杂问题简单化,拆解成更容易解决的问题。我们把它分解成两个简单问题:第一,**如何获得这些数据**;第二,**这些数据如何聚合和展现,为每个角色提供感兴趣的信息**。
先解决第一个问题也就是数据来源。Job模型在设计态和运行态下都是一棵Job树所有的数据也都依附在这棵Job树上。
那数据就有两个来源一个来源是在设计态下加到Job模型里的数据。比如测试Job和需求的关联这在Job设计时可以作为Job的一个属性RequirementID添加到Job模型里另一个来源是在运行态下采集到的数据。比如执行的检查点、测试Log、屏幕抓图等等采集到这些数据之后也可以添加到Job树里。
你可以想象这样的一个场景Job树从设计态到运行态是这棵树上的果实越来越多的过程。这里的果实就是数据刚开始时只有设计数据后来运行时数据也加进来了。参看下图更有助于你加深印象。
![图片](https://static001.geekbang.org/resource/image/3d/91/3dff8d185d218705001e0089d463bb91.jpg?wh=1920x834)
运行完毕后这棵Job树就结满了果实。虽然你不知道谁会要什么样的水果拼盘但你能通过根Job能遍历所有的果子能做什么样的菜自然就能心里有数了。
在每个Job节点执行相应的聚合算法就可以生成不同层级的报告了。抽象Job可以生成汇总报告而实例Job生成诊断报告。
现在我们再把Job的设计态、运行态、结果态理一遍。通过下图你会更清楚地看到这个数据采集到聚合的过程。
现在,你可能会问,我有了数据之后,到底怎么展现呢?这就涉及到数据工程的最后一个环节——数据可视化了。
传统的测试报告会通过HTML展现。我们的TestJobFile.xml是XML文件最后结果态Job树也是一个XML文件你可以考虑通过XML+XSLT来实现数据的处理和可变样式呈现。
如今的数据工程发展日新月异自动化测试报告可以看作是数据工程的一个细分环节。这块的解决方案有很多比如Grafana、Kibana、Tabelau等等。我在下一模块“数据度量”最后一讲里为你分享这个问题的实现思路和方法敬请期待。
## 小结
为了把Job设计方法落地到你的日常工作活动中我们用一讲的篇幅依次讲解了将设计文档化、代码化和生成运行报告的方法。
怎么去评审和交流Job设计方案这需要我们把Job设计文档化而且要让评审高效我们还要考虑好用什么数据格式呈现。
XMLYAML还是Json这三种方式都能通用互相转换找出适合你的团队的那一种。另外还要能自动验证设计文档的合规性这里的可用方法有Schema验证还有自开发Validator代码的办法。
怎么能让Job设计和代码实现保持一致不发生脱节呢这里我们要考虑两个方向一个是从设计到代码另外一个是从代码到设计。
我们探讨了Auto Gen Auto的实现思路还了解了一个模版利器**Mustache**你可以用它产生任何类型的代码Java、GoJavaScript甚至HTML等等。
另外还有一个轻量的思路就是设计和执行合二为一我们参考了Ant、Maven的运行机制把TestJobFile.xml作为JobRunner的一个执行配置文件直接就能跑起来。这样既能验证设计又能验证代码。
最后,测试报告作为自动化测试最重要的成果,怎么能把价值充分展现呢?我们从传统报告里分析了需求之后,加以突破升级,用数据工程的思维理了一下自动化测试运行过程。
**这个过程,本质上是一个设计数据、采集数据、聚合数据和展现数据的数据工程。**用好Job模型的设计树、运行树、结果树就能满足这个要求设计树里设计数据运行树采集数据结果树聚合数据。
通过今天的学习,想必你也感受到了,在自动化测试领域,创新并落地一个新的设计方法,要比实现某个垂直领域的工具自动化要复杂很多,要考虑的点也多,也需要不小的开发工作量。
感谢你学习完这一模块。为了奖励你的耐心也为了节省Job模型在你项目落地的时间我把Job模型框架的代码上传到 [GitHub](https://github.com/sheng-geek-zhuanlan/JobFramework) 里,你可以参考,按需取用。
## **思考题**
在你的项目里,自动化测试报告都有哪些角色要看?报告里都展现了哪些数据,是什么格式呈现的?
欢迎你在留言区跟我交流互动。如果觉得今天讲的方法对你有启发,也推荐你分享给更多朋友、同事,一起学习进步。