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開始構建