背景
client端执行webUI自动化测试时依赖操作系统上安装了对应的浏览器,并且浏览器与驱动要进行版本的对应——而selenium是可以部署集群,通过远程的方式执行对应的自动化用例,方便高效。
原理
1、通过代码可知,自动化运行的主类接收了任务id,获取到任务信息后根据任务类型判断进行接口测试 或 UI测试
public class RunAutomationTest extends TestControl { public static void main(String[] args) { try { PropertyConfigurator.configure(RunService.APPLICATION_HOME + File.separator + "log4j.conf"); String taskid = args[0]; TaskExecute task = GetServerApi.cgetTaskbyid(Integer.parseInt(taskid)); TaskScheduling taskScheduling = GetServerApi.cGetTaskSchedulingByTaskId(Integer.parseInt(taskid)); if (taskScheduling.getTaskType() == 0) { // 接口测试 TestControl.taskExecutionPlan(task); } else if (taskScheduling.getTaskType() == 1) { // UI测试 WebTestControl.taskExecutionPlan(task); } else if (taskScheduling.getTaskType() == 2) { AppTestControl.taskExecutionPlan(task); } } catch (Exception e) { // TODO Auto-generated catch block LogUtil.APP.error("启动测试任务运行主函数出现异常,请检查!",e); } finally{ System.exit(0); } } }
2、通过wd = WebDriverInitialization.setWebDriverForTask(drivertype) 这行代码可知,根据浏览器的类型获取了驱动。UI自动化浏览器类型 0 IE 1 火狐 2 谷歌 3 Edge 4 集群,其中4 集群是我自己添加的,用来标识远程集群。
public static void taskExecutionPlan(TaskExecute task) { // 记录日志到数据库 serverOperation.exetype = 0; String taskid = task.getTaskId().toString(); TestControl.TASKID = taskid; String restartstatus = RestartServerInitialization.restartServerRun(taskid); BuildResult buildResult = BuildingInitialization.buildingRun(taskid); List<ProjectCaseParams> pcplist = GetServerApi.cgetParamsByProjectid(task.getProjectId().toString()); TaskScheduling taskScheduling = GetServerApi.cGetTaskSchedulingByTaskId(task.getTaskId()); String projectname = taskScheduling.getProject().getProjectName(); task = GetServerApi.cgetTaskbyid(Integer.parseInt(taskid)); String jobname = taskScheduling.getSchedulingName(); int drivertype = serverOperation.querydrivertype(taskid); int[] tastcount; // 判断是否要自动重启TOMCAT if (restartstatus.contains("Status:true")) { // 判断是否构建是否成功 if (BuildResult.SUCCESS.equals(buildResult)) { WebDriver wd = null; try { wd = WebDriverInitialization.setWebDriverForTask(drivertype); } catch (WebDriverException e1) { LogUtil.APP.error("初始化WebDriver出错 WebDriverException!", e1); } catch (IOException e2) { LogUtil.APP.error("初始化WebDriver出错 IOException!", e2); } serverOperation caselog = new serverOperation(); List<ProjectCase> cases = GetServerApi.getCasesbyplanId(taskScheduling.getPlanId()); LogUtil.APP.info("当前测试任务 {} 中共有【{}】条待测试用例...",task.getTaskName(),cases.size()); serverOperation.updateTaskExecuteStatusIng(taskid, cases.size()); int i = 0; for (ProjectCase testcase : cases) { i++; LogUtil.APP.info("开始执行当前测试任务 {} 的第【{}】条测试用例:【{}】......",task.getTaskName(),i,testcase.getCaseSign()); List<ProjectCaseSteps> steps = GetServerApi.getStepsbycaseid(testcase.getCaseId()); if (steps.size() == 0) { continue; } try { // 插入开始执行的用例 caselog.insertTaskCaseExecute(taskid, taskScheduling.getProjectId(),testcase.getCaseId(),testcase.getCaseSign(), testcase.getCaseName(), 4); WebCaseExecution.caseExcution(testcase, steps, taskid, wd, caselog, pcplist); } catch (Exception e) { // TODO Auto-generated catch block LogUtil.APP.error("用户执行过程中抛出异常!", e); } LogUtil.APP.info("当前用例:【{}】执行完成......进入下一条",testcase.getCaseSign()); } tastcount = serverOperation.updateTaskExecuteData(taskid, cases.size(),2); String testtime = serverOperation.getTestTime(taskid); LogUtil.APP.info("当前项目【{}】测试计划中的用例已经全部执行完成...",projectname); MailSendInitialization.sendMailInitialization(HtmlMail.htmlSubjectFormat(jobname), HtmlMail.htmlContentFormat(tastcount, taskid, buildResult.toString(), restartstatus, testtime, jobname), taskid, taskScheduling, tastcount,testtime,buildResult.toString(),restartstatus); // 关闭浏览器 assert wd != null; wd.quit(); } else { LogUtil.APP.warn("项目构建失败,自动化测试自动退出!请前往JENKINS中检查项目构建情况。"); MailSendInitialization.sendMailInitialization(jobname, "构建项目过程中失败,自动化测试自动退出!请前去JENKINS查看构建情况!", taskid, taskScheduling, null,"0小时0分0秒",buildResult.toString(),restartstatus); } } else { LogUtil.APP.warn("项目TOMCAT重启失败,自动化测试自动退出!请检查项目TOMCAT运行情况。"); MailSendInitialization.sendMailInitialization(jobname, "项目TOMCAT重启失败,自动化测试自动退出!请检查项目TOMCAT运行情况!", taskid, taskScheduling, null,"0小时0分0秒",buildResult.toString(),restartstatus); } }
3、在类WebDriverInitialization的setWebDriverForTask方法中添加针对浏览器集群的驱动,代码如下,添加了else if通过配置生成浏览器驱动。
1 public static WebDriver setWebDriverForTask(int drivertype) throws WebDriverException,IOException{ 2 // 参数为空 3 String drivenpath= RunService.APPLICATION_HOME + File.separator+"BrowserDriven"+File.separator; 4 WebDriver webDriver = null; 5 LogUtil.APP.info("准备初始化WebDriver对象...检查到当前操作系统是:{}",OS); 6 if(drivertype==0){ 7 if(OS.startsWith("win")){ 8 System.setProperty("webdriver.ie.driver",drivenpath+"IEDriverServer.exe"); 9 webDriver = new InternetExplorerDriver(); 10 }else{ 11 LogUtil.APP.warn("当前操作系统无法进行IE浏览器的Web UI测试,请选择火狐或是谷歌浏览器!"); 12 } 13 }else if(drivertype==1){ 14 FirefoxOptions options = new FirefoxOptions(); 15 if(OS.startsWith("win")){ 16 System.setProperty("webdriver.gecko.driver",drivenpath+"geckodriver.exe"); 17 }else if(OS.contains("mac")){ 18 options.addArguments("start-maximized"); 19 System.setProperty("webdriver.gecko.driver",drivenpath+"geckodriver_mac"); 20 }else{ 21 LogUtil.APP.info("检测到当前系统环境是Linux,默认使用headless方式运行Firefox浏览器的Web UI自动化..."); 22 //无界面参数 23 options.setHeadless(true); 24 //禁用沙盒 25 options.addArguments("no-sandbox"); 26 options.addArguments("start-maximized"); 27 System.setProperty("webdriver.gecko.driver",drivenpath+"geckodriver_linux64"); 28 } 29 webDriver = new FirefoxDriver(options); 30 }else if(drivertype==2){ 31 ChromeOptions options = new ChromeOptions(); 32 if(OS.startsWith("win")){ 33 System.setProperty("webdriver.chrome.driver",drivenpath+"chromedriver.exe"); 34 }else if(OS.contains("mac")){ 35 options.addArguments("start-maximized"); 36 System.setProperty("webdriver.chrome.driver",drivenpath+"chromedriver_mac"); 37 }else{ 38 LogUtil.APP.info("检测到当前系统环境是Linux,默认使用headless方式运行Chrome浏览器的Web UI自动化..."); 39 //无界面参数 40 options.setHeadless(true); 41 //禁用沙盒 42 options.addArguments("no-sandbox"); 43 options.addArguments("start-maximized"); 44 System.setProperty("webdriver.chrome.driver",drivenpath+"chromedriver_linux64"); 45 } 46 webDriver = new ChromeDriver(options); 47 }else if(drivertype==3){ 48 if(OS.startsWith("win")){ 49 System.setProperty("webdriver.edge.driver",drivenpath+"msedgedriver.exe"); 50 webDriver = new EdgeDriver(); 51 }else if(OS.contains("mac")){ 52 System.setProperty("webdriver.edge.driver",drivenpath+"msedgedriver_mac"); 53 webDriver = new EdgeDriver(); 54 }else{ 55 LogUtil.APP.warn("当前操作系统无法进行Edge浏览器的Web UI测试,请选择火狐或是谷歌浏览器!"); 56 } 57 }else if(drivertype==4){ 58 // 返回配置好的remote 59 Properties properties = SysConfig.getConfiguration(); 60 String hubUrl = properties.getProperty("remote.webdriver.url"); 61 String browser = properties.getProperty("remote.webdriver.browser"); 62 LogUtil.APP.info("集群执行webUI:{},节点浏览器类型为:{}....",hubUrl,browser); 63 switch (browser){ 64 case "chrome": 65 webDriver = new RemoteWebDriver(new URL(hubUrl), DesiredCapabilities.chrome());break; 66 case "firefox": 67 webDriver = new RemoteWebDriver(new URL(hubUrl), DesiredCapabilities.firefox());break; 68 case "safari": 69 webDriver = new RemoteWebDriver(new URL(hubUrl), DesiredCapabilities.safari());break; 70 case "edge": 71 webDriver = new RemoteWebDriver(new URL(hubUrl), DesiredCapabilities.edge());break; 72 default: 73 LogUtil.APP.warn("集群中浏览器配置错误,默认使用chrome浏览器...",browser); 74 webDriver = new RemoteWebDriver(new URL(properties.getProperty("remote.webdriver.url")), DesiredCapabilities.chrome());break; 75 } 76 }else{ 77 LogUtil.APP.warn("浏览器类型标识:{},获取到的浏览器类型标识未定义,默认IE浏览器进行执行....",drivertype); 78 System.setProperty("webdriver.ie.driver",drivenpath+"IEDriverServer.exe"); 79 webDriver = new InternetExplorerDriver(); 80 } 81 82 //解决webdriver在unix环境中,最大化会出现异常的bug,unix最大化在options中单独设置 83 if(OS.startsWith("win")){ 84 assert webDriver != null; 85 webDriver.manage().window().maximize(); 86 } 87 88 //设置页面加载最大时长30秒 89 assert webDriver != null; 90 webDriver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS); 91 //设置元素出现最大时长30秒 92 webDriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); 93 94 return webDriver; 95 }
4、src/main/Resources/sys_config.properties配置中需要添加Remote webdriver的配置,告诉集群在哪儿&用哪种浏览器
remote.webdriver.url=http://xx.xx.xx.xx:4444/wd/hub/
remote.webdriver.browser=chrome
url是操作第一步中selenium集群所在的地址,能够直接访问
操作
1、docker搭建selenium集群——不要问为啥用docker,问就是因为docker简单稳定;若想远程调试可以安装debug版本,使用VNC调试。
# 创建docker网络 docker network create grid # 以docker容器的形式启动hub并暴露4444端口 docker run -d -p 4444:4444 --net grid --name selenium-hub selenium/hub:3.14.0-europium # 以docker容器的形式启动并挂载Chrome的Node docker run -d --net grid -e HUB_HOST=selenium-hub -v /dev/shm:/dev/shm selenium/node-chrome:3.14.0-europium # 以docker容器的形式启动并挂载Firefox的Node docker run -d --net grid -e HUB_HOST=selenium-hub -v /dev/shm:/dev/shm selenium/node-firefox:3.14.0-europium
2、Jenkins部署客户端(都是构建maven项目,参考web端部署)
创建maven项目流水线>源码管理填写client端git仓库及分支>前置脚本干掉进程>构建设置(同web端)>后置脚本启动程序
后置脚本如下
cp -r /home/jiayifei.1995/.jenkins/workspace/LuckyFrameClient/target/* /home/jiayifei.1995/deploy/LuckyFrameClient
BUILD_ID=donKillMe cd /home/jiayifei.1995/deploy/LuckyFrameClient/classes/ sh start_service.sh
注意事项
1、构建之后没有email_template目录咋整? 答:检查pom文件,看看是否将需要的目录放到<insides>标签里面了
<resource> <directory>src/main/Resources</directory> <includes> <include>**/*.cmd</include> <include>**/*.bat</include> <include>**/*.exe</include> <include>**/*.conf</include> <include>**/*.sh</include> <include>**/*.txt</include> <include>**/*.properties</include> <include>**/email_template/*.ftl</include> </includes> </resource>
2、我如何添加远程执行case呢?答:添加远程用例当然还得改下web端啦
src/main/resources/templates/testexecution/taskScheduling这个目录下的add与edit都需要添加下,改动部分代码如下
<div class="form-group" id="divBrowserType" style="display:none;"> <label class="col-sm-3 control-label">浏览器类型:</label> <div class="col-sm-8"> <label class="radio-box"> <input type="radio" name="browserType" value="0" th:checked="true"/> IE </label> <label class="radio-box"> <input type="radio" name="browserType" value="1" /> 火狐 </label> <label class="radio-box"> <input type="radio" name="browserType" value="2" /> 谷歌 </label> <label class="radio-box"> <input type="radio" name="browserType" value="3" /> Edge </label> <label class="radio-box"> <input type="radio" name="browserType" value="4" /> Remote </label> </div> </div>