持续集成 测试开发之路--分布式用例执行

孙高飞 · 2016年11月27日 · 最后由 孙高飞 回复于 2017年03月28日 · 2786 次阅读
本帖已被设为精华帖!

前言

这两天趁着有时间,我疯狂的码字了~~

背景

我们公司是做人工智能平台的,什么是人工智能呢? 大数据 + 机器学习。大数据运行的基本就不快。机器学习算法运行起来也是慢的让人泪流满面。在我们的集群配置下,我使用一个 5M 的数据从数据引入到数据处理,特征工程,模型训练和评估报告等等一整套线下模型调研流程要 10 分钟左右。所以单线程执行测试用例是不靠谱的。而 UI 自动化在一台机器上只能是单线程执行。所以多台机器同时运行 case 的分布式处理方案呼之欲出。

原理

原理其实很简单。 我分步骤说一下

  1. 我们是用 jenkins 做 CI 工具的。所以通过 jenkins 的 slave 机制添加多台 slave 机器当做测试机。配置测试机在同一时间只能运行一个任务,也就是不能并发执行。
  2. 创建一个可以并发的 job 然后把步骤一中的测试机注册到这个 job 中,我们姑且叫这个 job 为 run job。run job 在触发构建的时候在所有的测试机中查询空闲的测试机,如果有空闲的就到这台机器上执行测试任务。如果没有空闲的机器就等待直到有机器空闲。
  3. 我们把测试用例分成不同的 group,然后创建一个分发任务的 job,我们姑且叫这个 job 为 dispatch job。dispatch job 中配置一段 shell 脚本,通过 jenkins API 的方式调用 run job 并把 group 作为参数传递过去。这样我们在 dispatch job 中调用了 N 次 run job 也就是在不同的机器上同时跑不同的 group。
  4. 接下来我们要解决测试报告的问题。我要创建一个归集所有测试结果的 job,我们姑且叫它为 results job。results job 会拉取所有 run job 的测试结果并保存到特定位置
  5. 然后我们创建一个 merge 测试结果并生成测试报告的 job,我们姑且叫它为 report job。

总结一下我们需要多台测试机器和 4 个 job。分别是 dispatch job,run job,results job 和 report job。 大概的样子如下:

配置 slave 测试机

这个比较简单,大家可以到网上找详细的教程。只需要记得创建的 node 要统一一个 label 比较好管理。如下:

dispatch job

这个 job 基本就是由 shell 脚本构成的,通过 shell 来调用 jenkins 的 API。如下:

#!/bin/sh

#输出构建者的信息
echo $BUILD_USER_EMAIL $BUILD_USER > tigger_user_info

#检查是否有任务还在跑
STATUS=`curl -v --silent "http://jenkins.4paradigm.com/job/prophet-test-run/api/json?tree=builds\[result\]" 2>&1 | grep -o "{\"result\":null}"`

#测试任务URL
RUN_JOB_URL="http://jenkins.4paradigm.com/job/prophet-test-run/buildWithParameters?token=prophet"

if [ "$STATUS" = "" ]; then
  curl "$RUN_JOB_URL&env=$env&branch=$branch&group=atomTest.xml"
  curl "$RUN_JOB_URL&env=$env&branch=$branch&group=model_train.xml"
  curl "$RUN_JOB_URL&env=$env&branch=$branch&group=dataload.xml"
  curl "$RUN_JOB_URL&env=$env&branch=$branch&group=smoke.xml"
  printf "下发测试任务成功!\n"
else
  printf "测试服务器繁忙,下发测试任务失败!\n"
  exit -1
fi

#任务下发成功后删除上次构建产生的结果
rm -rf  ../../prophet-send-report/workspace/allure-results/*

逻辑比较简单,首先检查当前还有没有 run job 在执行,如果有,那么执行失败。其实应该等待的执行结束的,只是暂时还没加这段逻辑。如果没有 run job 在执行。那么调用 run job 的 API 把测试任务分发下去。其中要把一些参数传递过去。例如运行环境,测试代码分支,测试用例的 group

run job

这个 job 比较常规,配置有点多我不详细的列举了。无非就是配置 git lab,拉取测试代码并 build 运行测试。其中 slave 测试机是注册在这个 job 里的。有一个需要注意的地方是 run job 运行结束后触发下一个 results job,需要传递一个比较重要的参数-- build number。因为下一个 job 需要知道 run job 的 build number 好去归集测试结果。如下:

对了还有一个特别重要的东西。为了要把测试结果从 slave 测试机上拉到 jenkins 上。我们需要剑走偏锋。配置如下:

其实这个时候我们并不希望在 run job 上就生成 html 的 report,我们指向的目录也不是 report 的目录。而是存放测试结果的 xml 文件的目录。jenkins 默认是不会从 slave 测试机上拉取这些文件的。为了让 jenkins 拉取这些文件,所以我们利用这个 html report 插件强行让 jenkins 把他们拉取上来。

results job

这也是一个有 shell 构成的 job。脚本如下:

#!/bin/sh

sleep 5s
# 通过run job传递的build number 将测试结果保存到特定目录下
folder="../../prophet-test-run/builds/$upstream_build/htmlreports/HTML_Report" 
if [ -d "$folder" ]; then
  cp -r "$folder" ../../prophet-send-report/workspace/allure-results/$upstream_build
fi


#获取测试执行Job里是否有未完成的任务
STATUS=`curl -v --silent "http://jenkins.4paradigm.com/job/prophet-test-run/api/json?tree=builds\[result\]" 2>&1 | grep -o "{\"result\":null}"`

if [ "$STATUS" = "" ]; then

  #获取构建人信息
  user_email=`awk '{print $1}' ../../prophet-task-dispatch/workspace/tigger_user_info`
  user_name=`awk '{print $2}' ../../prophet-task-dispatch/workspace/tigger_user_info`

  #邮件任务URL
  EMAIL_JOB_URL="http://jenkins.4paradigm.com/job/prophet-send-report/buildWithParameters?token=report"

  #触发发送报告任务
  curl "$EMAIL_JOB_URL&user_email=$user_email&user_name=$user_name"
  printf "下发邮件发送任务!\n"
else
  printf "还有测试任务正在执行!\n"
fi

逻辑也十分简单,根据 run job 传递 build number,copy 测试结果到特定的目录。 这里简单说明一下 jenkins 的目录结构。每个 job 都是存放在一个叫 jobs 的目录下,job 目录里面有 builds 和 workspace 两个文件夹。builds 目录分别存放每一个 build 的执行结果,results job 就是跑去 run job 的 builds 目录 copy 执行结果到 reports 的 workspace 目录下 (run job 传递过来的 build number 起作用了). workspace 目录就是 jenkins 上看到的工作控件了。我们 shell 就是在这个目录下运行的。 还有一个事,每个 run job 都会调用 results job 的。但是我们只希望最后一个 run job 结束后才让 results job 调用下游的 report job 来生成 report 并发送邮件。所以我们的 shell 脚本中才会有判断当前还有没有 run job 运行的逻辑。没有了才会调用 report job

report job

终于到了 report job。我们做了一堆配置,写了一堆脚本就是为了能成功合并测试结果并生成 html 的 report。这个 job 的任务非常简单,results job 已经把之前运行的所有测试的结果 copy 到了 report job 的 workspace 下。所以它要做的事情只有两件事,合并测试结果生成 report 以及发送邮件。 由于我用的 report 框架是 allure,所以直接用的 jenkins 上针对这个框架的插件。所以配置基本是很简单的。如下:

每一种 report 框架都会有 merge 测试结果的机制。大家根据自己的使用情况选择吧。

澄清

我澄清一下我使用的框架吧。 语言是 java,框架主要用的 testng,selenide,maven。 report 框架为 allure。所以才有上面一整套的配置。如果大家用的技术体系不一样,上面的分发任务和生成 report 的配置可能是不一样的。

关于 report 框架:allure

这个所谓的分布式执行处理的最大的难点其实就是测试结果的归集和 html report 的生成。我们起码有一半的工作都是为了达到这个目的。所以选取一个好的 report 框架是比较重要的。因为我是 java 系么。所以选择了 allure。allure 在 merge 测试结果方面挺方便的,同时我觉得 allure 是流程测试中尤其是 UI 自动化测试中最好的 report 框架。所以在这里还是小推销一下。它的效果图如下:

可以看到它不仅有强大的测试分类功能。还可以详细的记录你每一步的操作并展示在 report 中。显示截图的功能也很棒。另外还有不同维度统计功能。例如这个时间统计:

可以看到它展示了每台 slave 测试机上的 case 运行时间。可以让你调整用例的分组策略。不会让某一个 group 运行时间过长影响整体运行效率。下面是只列出失败的 case:

下面是统计仪表盘:

具体的使用攻略大家翻一翻我之前的帖子吧。我有一些详细说明。 也可以到 git hub 上直接看官方的 wiki。

总结

OK 今天爆发了,连写了 3 篇。 之前大概快一个月没写文章了。今天就当补上了吧。其实这个方案是用 jenkins 的机制临时糊出来分布式执行。记得之前有人分享过自己开发的分布式框架。我感觉成本还是太高了,需要开发比较长的一段时间。 虽然用 jenkins 糊出来的这个方案看上去有点不伦不类的,但是够用。搭建起来也快,按着这篇文章搞,估计一两天也就把坑踩完,投入使用了。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 15 条回复 时间 点赞

我现在做的是多进程启动多个 appium 执行,用分布式来弄,优点在哪里?一直不懂这块……

—— 来自 TesterHome 官方 安卓客户端

#1 楼 @lose 我主要的目的就是增加执行速度么。分布式中的每台机器运行的 case 都是不一样的。我做 PC 端的 web UI 自动化没办法在一台机器上起多个浏览器进行测试。以前尝试过多个浏览器并发运行会出问题。你的多进程启动多个 appium,也是在不同的手机上吧?

#2 楼 @ycwdaaaa 学习了

—— 来自 TesterHome 官方 安卓客户端

#3 楼 @lose 你做的那个主要是解决兼容性测试么?

不错,正在尝试使用 jenkins

思寒_seveniruby 将本帖设为了精华贴 11月28日 02:51

#4 楼 @ycwdaaaa 嗯,有这方面的考虑,等我的那个弄好了,到时候也想研究下分布式😁

多个任务能运行到一个机器上面吗

#7 楼 @lose 搞完了分享一下哈

#8 楼 @cobbxia 有一个队列,排队等待机器的空闲。就像是一个生产着消费者模式一样。所以多个任务是可以运行在一台机器上的。但是没办法在一台机器上同时执行

孙大师,其实我想说,run job 不清楚☺

—— 来自 TesterHome 官方 安卓客户端

#11 楼 @hu_qingen 恩恩,哪方面不清楚,我再解释解释

如果是 browserstack 之类的云测试平台的话,应该可以用启动多台实例的方式完成在不同机器上执行不同用例的功能。而且还外加各种浏览器和系统支持哟~

#13 楼 @xuxtc 分布式执行向来不是问题,问题是执行结束后把测试结果归集起来并生成 report

最近在看您写的文章,感觉受益良多,想在这里请教个问题,如果是大批量的用例同时分布执行,用例之间的数据相互影响结果验证该怎么解决比较好呢?每一套测试机都要一套测试环境么?如果只有一套测试环境怎么处理比较好呢,多谢

sjh 回复

case 之间不要做成相互依赖的。 如果一定要相互依赖,也要确保相互依赖的 case 在一台机器上有序的运行

ABEE ycwdaaaa (孙高飞) 在 TesterHome 的发帖整理 中提及了此贴 01月12日 13:47
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册