1一个极简的自动化测试平台需要什么功能?
从测试用例管理的角度来看,测试平台或者测试框架,首先需要解决业务域的问题 1)如何来表征一个测试用例、步骤以及用例集 2)如何来执行用例、用例集 3)如何来获取用例结果并登记入库
传统用例平台的做法
常见的测试用例平台,无论采取什么样的技术和测试工具来实现,基本是这样的一个实现:
- 平台定义了用例和用例集:用户在前端进行用例(集)的CURD,通过自定义的DSL来描述自动化用例。
- 平台发起用例执行并获得结果:通过用例执行模块,通过前台人员触发或者外部接口触发的方式,来执行指定的用例(集)并获得测试结果。
SonarQube的启示
SonarQube 在其官网提供了如下的一个架构
image.png
虽然我们还是有一个中心化的SonarQube服务器来分析和展示扫描结果,与传统用例平台相比,我们看到了2个很大的区别, 1)扫描任务是由客户端Scanner主动发起的。传统测试平台的测试任务往往是由服务端发起。 2)客户端和服务端通过服务接口进行交互,服务接口屏蔽了语言和工具的差异,也就出现了各种不同的扫描器,来支持几十种开发语言和各种构建工具和IDE。 而传统用例平台,则往往会自定义统一的DSL来描述测试用例,对于跨工具的支持考虑较少。
2去中心化的测试用例管理平台
手工与自动化一体
在DevOps工程实践,我们自行设计了用例管理模块,作为整个DevOps平台的一部分,用于统一管理手工和自动化用例以及执行结果和报告度量。希望利用平台整合用例上下游数据的优势,来形成协同效应,提高用例管理模块的易用性,吸引用户使用,从而支持平台“软件定义流程”目标的实现。
去中心化
这其中,对于自动化用例这块,我们采取的是类似SonarQube的设计,无论是手工用例还是自动化用例,在用例管理模块中只是用例这个业务对象中某些属性的差别,模块和平台自身并不提供DSL,用例代码或者脚本以及驱动等托管在各自项目库中。
3通过Maven插件实现用例管理
业务需求
- 能解析Maven Surefile/FaleSafe的测试结果报告
- 持DryRun,也就是将解析结果输出到本地文件
- 能向指定的用例管理服务报送测试用例清单,更新用例
- 能向指定的用例管理服务报送用例执行结果
开发需求
- Maven Surefire测试报告的解析,获得用例集和测试结果
- 调用服务端公布的用例上报和结果上报的接口
- 写本地文件
- 入参解析
Surefile报告解析
作为面向GitHub编程的测试工程师,在明确了需求之后,当然是先看看有没有现成或者类似的项目可以参考,因为类似这种需求肯定是有业界同仁已经遇到和解决过的。 通过搜索,发现原来maven-surefire自己就提供了一个 surefire-report-parser 通过简单的几行代码,就可以实现对测试报告的解析。
代码语言:javascript复制 SurefireReportParser report =
new SurefireReportParser(
singletonList( getTestDir() ), ENGLISH, new NullConsoleLogger() );
List<ReportTestSuite> suites = report.parseXMLReportFiles();
在ReportTestSuite的对象中有提供如下的获取测试用例的方法
代码语言:javascript复制 public List<ReportTestCase> getTestCases()
{
return testCases;
}
通过聚合上述的suites 中的testCases,就可以完整获得某次执行的用例以及执行结果了。 用官方提供的解析方案,比自行进行xml文件解析还是要简单很多,兼容性和稳定性上面也有保障。
Maven插件
完成了用例的解析之后,接着就是Maven插件的开发了,这部分简单来说,就是要编写一个类来继承extends AbstractMojo,并且实现父类中的execute()方法。 本着拿来主义的思想,笔者又再次面向github找到了一个需求契合度非常高的项目summary-maven-plugin 这个项目也是分析用例执行结果,然后将统计结果打印在console上。
代码语言:javascript复制[INFO] --- summary-maven-plugin:1.0.0-SNAPSHOT:
summary (default-cli) @ m-connect ---
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] | Test Report |
[INFO] ------------------------------------------------------------------------
[WARNING] [com.namics.oss.FailingTest.somethingThatFailes:22]
java.lang.AssertionError:
Expected: <10>
but: was <15>
at com.namics.oss.FailingTest.somethingThatFailes(FailingTest.java:22)
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] total : 989
[INFO] errors : 0
[ERROR] failed : 1
[INFO] skipped : 8
[INFO] success : 99.09% in 19.06 s
[INFO] ------------------------------------------------------------------------
与这个项目类似,将解析报告获取的List<TestSuite> suites 中的List<ReportTestCase> testcases 进行转换,变成符合平台自身定义的List<TestCase> testcases 就可以完成数据的获取。剩下的就是dryrun写本地文件或者通过http client对外发送了。
用例转换
在SummaryMojo中,核心的代码是获取报告用例集并转换成平台定义的用例集格式。
代码语言:javascript复制 @Override
public void execute() throws MojoExecutionException {
if (!hasReportDirectories()) {
return;
}
logger = new PluginConsoleLogger(getLog());
List<ReportTestSuite> reportTestSuites = new
SurefireReportAnalyser().parse(getReportsDirectories(), logger);
List<TestCase> testCases=getAllTestCases(reportTestSuites);
//....... dryrun 及 post部分略
发挥作用的是getAllTestCases这个转换方法,
代码语言:javascript复制 private List<TestCase> getAllTestCases(List<ReportTestSuite> reportTestSuites) {
List<TestCase> testCases = reportTestSuites.stream()
.flatMap(testSuite -> testSuite.getTestCases().stream())
.map(tc -> SureFireAdaptor.convertTestCase(tc,prefix,sut))
.collect(Collectors.toList());
return testCases;
}
具体在转换时,因为两边设计的不同,需要进行一些调整,因此引入了一个SureFireAdaptor类来处理这些事情。只获取用例名称和运行结果,简单来看是这样的,
代码语言:javascript复制public class SureFireAdaptor {
public static TestCase convertTestCase(ReportTestCase
reportTestCase, String prefix, String sut){
TestCase testCase=new TestCase();
if(reportTestCase !=null){
testCase.setCaseName(filterClassName(
reportTestCase.getFullName(),prefix,sut));
testCase.setResult(checkResult(reportTestCase));
}
return testCase;
}
private static String checkResult(ReportTestCase reportTestCase){
String result="pending";
if(reportTestCase.hasFailure()){
result="fail";
} else {
result="pass";
}
return result;
}
private static String filterClassName(String className, String
prefix, String sut){
String temp=className;
String filter=className;
if(prefix !=null && !prefix.isEmpty()){
if(!prefix.endsWith(".")){
prefix =".";
}
temp=className.replaceFirst(prefix,"");
}
if(sut !=null && !sut.isEmpty()){
if(!sut.endsWith(".")){
sut =".";
}
filter=sut temp;
}
return filter;
}
其中的主要调整项是:
- 用FullName(包 类 方法)作为CaseName
- 将包的前缀部分,例如com.github过滤掉
- 将系统名叠加在CaseName的最前面。
最终我们得到的是一个CaseName将在后台被解析出系统名、用例层级结构和用例名称这样三项内容。
dryrun及post部分略
这部分主要是将解析处理好的内容打印到本地文件或者调用用例管理平台API上传用例或者结果,限于篇幅就省略了。
运行
参考上述项目,将修改后的summary-manven-plugin 在(测试)项目pom.xml文件的<build></build>部分引入进来
代码语言:javascript复制<build>
<plugins>
<plugin>
<groupId>com.namics.oss.maven</groupId>
<artifactId>summary-maven-plugin</artifactId>
<version>1.0.0</version>
</plugin>
</plugins>
</build>
然后执行
代码语言:javascript复制mvn summary:summary
即可完成。如果各项参数,如sut 可以通过pom.xml中设置,或者是通过-D来动态输入。
实际工作中,一个(测试)项目可以自行托管在代码库中,用例有新增修改,并通过CI或者其它方式执行之后,可以自动或者按需将用例和执行结果提交到测试用例管理平台,从而生成度量数据和管理报告。