Selenium Web 自动化 - 项目持续集成(进阶)
Selenium Web 自动化 - 项目持续集成(进阶)
2017-03-09
1 背景及目标
2 环境配置
2.1 SVN的安装及使用
2.2 新建Jenkins任务
3 过程分析
1 背景及目标
上一篇文章Selenium Web 自动化 - 项目持续集成中用到Jenkins+Git实现持续集成。可以实现自动化部署、运行、发送运行结果。但这里还有几个问题:
- 当有新的用例,我们除了要添加两个Excel(一个用于定位元素,一个用于记录操作步骤和操作数据),还要生成unittest的代码文件。
- 让后把两个excel和生成的unittest代码文件checkin到git。
由于某些测试人员没有开发经验,生成unittest代码文件,可能会出错。所以,我们的这篇文章的实现这样的目标:
当有新的用例(两个excel)创建且checkin的时候,jenkins能调用命令生成unittest代码文件并checkin到git(这里是commit到svn),且jenkins开始构建。
2 环境配置
2.1 SVN的安装及使用
SVN客户端:TortoiseSVN使用详细步骤
SVN服务器端:用VisualSVN做项目版本控制
配置下SVN服务器端,如下图所示,详情见用VisualSVN做项目版本控制
图1 配置SVN服务器端
注意:上图中用户Developer1在2.2节Step2命令svn commit中会用到,在step3jenkin任务配置中也会用到。
2.2 新建Jenkins任务
Step1:选择SVN,设置Repository URL和Credentials;
注意:要显示Credentials,需安装插件“Subversion Plug-in”
Step2:配置PreSteps,输入命令GernerateCodeAndCommit.bat;
::设置svn客户端代码为当前目录 cd D:\Study\QkHttpTest ::运行可执行jar包 java -jar TCGenerateUnitCode.jar DR ::将目录下所有新增java文件标记为add svn add src\com\qf\test\unittest\*.java ::将标记为add或修改的java文件commit,注意:这里要添加用户名、密码,否则jekins没有权限add,会抛错:svn: E215004: No more credentials or we tried too many times。查看jenkens问题,见https://issues.jenkins-ci.org/browse/JENKINS-14781 svn --username Developer1 --password Developer1 commit -m "add unittest files" src\com\qf\test\unittest\*.java
这里的“TCGenerateUnitCode.jar”会做如下操作:
- 输入文件名作为参数,比如:DR
- 把DR和test\TestCase拼接成新的目录test\TestCase\DR,在这个目录下读取所有testcase,并生成unittest入口的java文件
- 把这个文件放到src/com/qf/test/unittest下
TCGenerateUnitCode代码如下:
package com.qf.test.util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.util.List; import java.util.Scanner; import org.testng.Assert; import org.testng.DependencyMap; import com.qf.test.dal.RWTxtFileWithJackson; import com.qf.test.dal.RWTxtFileWithBuffer; import com.qf.test.entity.Dependence; import com.qf.test.entity.TestCase; /* * 输入文件名作为参数,比如:DR * 把DR和test\TestCase拼接成新的目录test\TestCase\DR,在这个目录下读取所有testcase,并生成unittest入口的java文件 * 把这个文件放到src/com/qf/test/unittest下 */ public class TestCaseFactoryForModule { static String caseFolder = "test\\TestCases"; public static void main(String[] args) throws Exception { final String casePageFolder = "src/com/qf/test/unittest"; String moduleName = null; File sourceFile = null; // @SuppressWarnings("resource") // // 从控制台可以输入 // Scanner s = new Scanner(System.in); // System.out.println("请输入模块名称(不要按回车键,输入完成之后请再按回车键):"); // moduleName = s.nextLine();// 输入模块名字 moduleName = args[0]; moduleName = moduleName.replaceFirst(moduleName.substring(0, 1), moduleName.substring(0, 1).toLowerCase()); // 如果包名不存在,就新建 // File functionPackage = new File(caseFolder + "/" + moduleName); File functionPackage = new File(casePageFolder); if (functionPackage.exists()) { System.out.println(functionPackage + "包已经存在,自动跳过!"); System.out.println("正在生成用例到" + moduleName + "包下,请稍等..."); } else { functionPackage.mkdir(); System.out.println(functionPackage + "包已创建!"); System.out.println("正在生成用例到" + moduleName + "包下,请稍等..."); } String functionName = null; sourceFile = new File(casePageFolder + File.separator + moduleName.toUpperCase() + "_Test.java");// 创建测试用例源码,指定存放路径 try { FileWriter writer = new FileWriter(sourceFile); // 生成测试用例代码的头文件 writer.write("package com.qf.test.unittest; \n" + "import org.testng.annotations.Test; \n" + "import com.qf.test.unittest.base.BaseParpare; \n " + "import com.qf.test.bll.TestProcess; \n" + "public class " + moduleName.toUpperCase() + "_Test extends BaseParpare{ \n"); for (int i = 0; i < getFunctionNum(moduleName); i++) { // 第一层循环 // 取得模块的个数 functionName = getFunctionName(moduleName, i);// 获得每轮循环的 // 模块名 TestCase tc = RWTxtFileWithJackson.Read(caseFolder + "\\" + moduleName + "\\" + functionName + ".csv"); Dependence[] dep = tc.getDependencies(); functionName = functionName.replaceFirst( functionName.substring(0, 1), functionName.substring(0, 1).toLowerCase()); // @Test(dependsOnMethods = {"PostwithImages"}) String testcase = null; String testcases = ""; if (dep != null) { for (int k = 0; k < dep.length; k++) { testcase = dep[k].getTestcase(); testcase = testcase.replaceFirst( testcase.substring(0, 1), testcase.substring(0, 1).toLowerCase()) + "_Test"; if (testcases != "") testcases = testcases + "\",\"" + testcase; else testcases = testcase; } testcases = "(dependsOnMethods = {\"" + testcases + "\"})"; } // @Test的主体部分,也就是测试用例的方法 writer.write("@Test" + testcases + "\npublic void" + " " + functionName + "_Test() {\n" + "\tTestProcess.Run(testcasefolderPath," + "\"" + moduleName.toUpperCase() + "\",\"" + functionName.replaceFirst(functionName .substring(0, 1), functionName.substring(0, 1) .toUpperCase()) + "\");\n" + " }\n"); } // 代码结尾大括号 writer.write("}"); writer.close(); } catch (IOException ex) { System.out.println("Error: " + functionName + "\n" + ex.getMessage()); return; } System.out.println("模块[" + moduleName + "] 的用例已经生成完毕,共计:" + getFunctionNum(moduleName) + "条,请到" + casePageFolder + "/" + "路径下查阅!"); } /** * 获得当前路径下模块个数 * * @return 得到模块的个数 */ public static int getFunctionNum(String moduleName) { int countNotfile = 0; String path = caseFolder + "\\" + moduleName; File file = new File(path); File[] array = file.listFiles(); for (int i = 0; i < array.length; i++) { if (!array[i].isFile()) countNotfile = countNotfile + 1; } return array.length - countNotfile; } /** * 获得模块名字 也就是excel 表名 * * @param 循环模块名称的角标 * @return 得到对应index的模块名字 */ public static String getFunctionName(String moduleName, int index) { int countNotfile = 0; String path = caseFolder + "\\" + moduleName; // path="D:\\Program Files (x86)\\Jenkins\\jobs\\SvnQkHttpTest\\workspace\\test\\TestCases\\DR"; String functionName = ""; // get file list where the path has File file = new File(path); // get the folder list File[] array = file.listFiles(); for (int i = 0; i <= index; i++) { if (!array[i].isFile()) countNotfile = countNotfile + 1; } index += countNotfile; if (array[index].isFile()) { functionName = array[index].getName().substring(0, array[index].getName().lastIndexOf(".")); } return functionName; } }
Step3:设置触发条件,选择Poll SCM,日程表* * * * *,表示每当每隔1分钟扫描svn,若有commit操作,开始构建。
图2 新建Jenkins任务
3 过程分析
1 用户新增或修改用例(参数)并commit后
2 Jenkins会执行PreStep,即调用命令GenerateCodeAndCommit.bat。这里有三种情况:
- 若新建testcase目录并加入testcase,会生成新的unittest入口java文件,svn把这个文件标记为add并commit;
- 若在已有的testcase目录加入testcase,会修改已有unittest入口Java文件,svn直接commit这个文件;
- 若只修改已有testcase,已有unittest入口java文件不变,不做svn操作。
3 Jenkins开始构建