[Java版]selenium关键字驱动框架设计实战(一)_职说测试-CSDN博客
前面已经带领大家认识了java反射,也演示了反射的调用示例,关键字的封装、更有关键字框架的代码实现;接下来就是对这个框架的具体实现进行封装。
<!-- excel读写工具包 --> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency>
如何设计excel用例呢,首先1条用例是1个场景,实现场景的代码有很多,譬如:用户登录系统,进入某模块,发布1条消息。又要考虑一些场景:有些时候我并不想执行。这就是TestSuit和testcase的关系
看到上面的设计之后肯定又需要做进一步优化,因为在testsuit找到要执行的用例,需要去teststep中执行步骤,那么不是所有场景的用例都写在teststeps吧,那么变换一下,将testsuiteId作为teststep的sheet名
/** * * TODO:操作excel * * @author Joe-Tester * @time 2021年8月27日 * @file ExcelUtils.java */ public class ExcelUtils { public static HSSFSheet ExcelSheet; public static HSSFWorkbook ExcelBook; public static HSSFRow Row; public static HSSFCell Cell; /** * 加载Excel * * @param Path * :文件路径 */ public static void setExcelFile(String Path) { FileInputStream ExcelFile; try { ExcelFile = new FileInputStream(Path); ExcelBook = new HSSFWorkbook(ExcelFile); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 结果回写excel * * @param Result * :执行结果回写 * @param RowNum * :每次判断关键字的行号 * @param ColNum * : 固定的结果列 * @param Path * :文件路径 * @param SheetName * :sheet页 */ public static void setCellData(String Result, int RowNum, int ColNum, String Path, String SheetName) { try { // 获取到excel的sheet表单 ExcelSheet = ExcelBook.getSheet(SheetName); // 获取sheet表单的行数 Row = ExcelSheet.getRow(RowNum); // 行+列确定单元格 Cell = Row.getCell(ColNum, MissingCellPolicy.RETURN_BLANK_AS_NULL); // 结果写入,如果没有单元格则创建单元格 if (Cell == null) { Cell = Row.createCell(ColNum); Cell.setCellValue(Result); } else { Cell.setCellValue(Result); } // 这个操作其实是重新创建了一个excel FileOutputStream fileOut = new FileOutputStream(Path); ExcelBook.write(fileOut); fileOut.flush();// 这个特别注意,需要刷新一下数据,相当于保存 fileOut.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 获取excel表格数据 * * @param RowNum * @param CloNum * @param SheetName * @return */ public static String getCellDate(int RowNum, int CloNum, String SheetName) { ExcelSheet = ExcelBook.getSheet(SheetName); Cell = ExcelSheet.getRow(RowNum).getCell(CloNum); // 万一单元格出现int类型,获取却不是String,先设置其单元格值为String;或者在单元格写入数字前单引号' Cell.setCellType(CellType.STRING); String cellData = Cell.getStringCellValue(); return cellData; } /** * 获取最后sheet最后一行 * * @param SheetName * @return */ public static int getLastRowNums(String SheetName) { try { ExcelSheet = ExcelBook.getSheet(SheetName); int rowCount = ExcelSheet.getPhysicalNumberOfRows(); // getLastRowNum()+1;//最后一行行标,比行数小1 return rowCount; } catch (Exception e) { throw (e); } } /** * * @param filePath * @param sheetName * @param picName */ public static void insertPicExcel(String filePath, String sheetName) { FileOutputStream fileOut = null; BufferedImage bufferImg = null; // 先把读进来的图片放到一个ByteArrayOutputStream中,以便产生ByteArray try { ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); bufferImg = ImageIO.read(new File(filePath)); ImageIO.write(bufferImg, "png", byteArrayOut); HSSFSheet testStepSheet = ExcelBook.getSheet(sheetName); // 画图的顶级管理器,一个sheet只能获取一个(一定要注意这点) HSSFPatriarch patriarch = testStepSheet.createDrawingPatriarch(); // anchor主要用于设置图片的属性 HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 255, 255, (short) 1, 1, (short) 5, 8); anchor.setAnchorType(AnchorType.DONT_MOVE_AND_RESIZE); // setAnchorType(3)不支持这个数值了 // 插入图片 patriarch .createPicture(anchor, ExcelBook.addPicture( byteArrayOut.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG)); fileOut = new FileOutputStream(Contants.excelFile + Contants.excelName); // 写入excel文件 ExcelBook.write(fileOut); } catch (Exception e) { e.printStackTrace(); } finally { if (fileOut != null) { try { fileOut.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
/** * * TODO:这个类的处理逻辑是和Excel表格对应的,如果要更改Excel只需要改这个类,或者新增加一个和Excel对应的类 * * @author Joe-Tester * @time 2021年8月27日 * @file StartEngine.java */ public class StartEngine { public static String Keywords = null; public static String r; public static boolean bResult; public static void StartEngine(Object actionKeyWords) throws IOException { // 初始化结果状态 bResult = true; // 打开excel用例文件 ExcelUtils.setExcelFile(Contants.excelFile + Contants.excelName); // 获取执行场景总行数 for (int j = 1; j < ExcelUtils.getLastRowNums(Contants.suitSheet); j++) { // 是否需要执行 String caseStatus = ExcelUtils.getCellDate(j, Contants.suitRunmode, Contants.suitSheet); // 获取场景的id作为操作步骤的sheet String testStepsSheet = ExcelUtils.getCellDate(j, Contants.suitTestSuiteId, Contants.suitSheet); int row; // 遍历场景行数,判断是否需要执行 if (caseStatus.equals("YES")) { Log.startTestCase("执行第" + j + "条用例:" + testStepsSheet); // 遍历操作步骤的关键字及操作元素 // Log.info(suitTestSuiteId+",sheet页,最后一行的行号:"+ExcelUtils.getLastRowNums(suitTestSuiteId)); for (row = 0; row < ExcelUtils.getLastRowNums(testStepsSheet); row++) { // 操作的关键字 String Keywords = ExcelUtils.getCellDate(row, Contants.excelKWCloNum, testStepsSheet); // 操作的页面元素 String r = ExcelUtils.getCellDate(row, Contants.excelPOCloNum, testStepsSheet); // 操作的值 String v = ExcelUtils.getCellDate(row, Contants.excelVaCloNum, testStepsSheet); // 调用关键字,注意suitTestSuiteId作为用例步骤的sheet页签的名字 Common_Engine.Action(Keywords, actionKeyWords, r, v, row, bResult, testStepsSheet); // 回写用例步骤页 if (bResult == false) { ExcelUtils.setCellData(Contants.fail, j, Contants.suitResult, Contants.excelFile + Contants.excelName, Contants.suitSheet); String destination = Screenshots.takeScreenshot( KeywordsDriven.driver, "error"); ExcelUtils.insertPicExcel(destination, testStepsSheet); } } Log.endTestCase("执行第" + j + "条用例:" + testStepsSheet + "结束!!!"); // 回写suit页 if (bResult == true) { ExcelUtils.setCellData(Contants.pass, j, Contants.suitResult, Contants.excelFile + Contants.excelName, Contants.suitSheet); } } else { Log.info("当前用例:" + testStepsSheet + ",执行状态为:" + caseStatus + ",不执行!!!"); // 如果出现YES-NO-YES的用例场景,在第一个NO之后的YES或者NO都不执行 // break; } } } }
最后完善Common_Engine部分的关键字调用方法,通过关键字的类对象来进行反射调用
/** * * TODO:通用引擎,执行的是test steps关键字操作步骤,java反射获取关键字方法 * * @author Joe-Tester * @time 2021年8月27日 * @file Common_Engine.java */ public class Common_Engine { // 日志收集器 // private static final Logger log = // LogManager.getLogger(Common_Engine.class.getName()); /** * * @param Keywords * :读取excel中关键字,判断是否在关键字对象中 * @param actionKeyWords * :关键字对象 * @param element * :关键操作对象的元素 * @param value * :输入值 * @param rowNum * : 操作步骤的行号 * @param bResult * : 结果 * @param SheetName * :执行场景步骤sheet页 */ public static void Action(String Keywords, Object actionKeyWords, String element, String value, int rowNum, boolean bResult, String SheetName) { // java反射获取类对象的所有方法 /* actionKeyWords= new Keywords_Driven(); */ Method[] method = actionKeyWords.getClass().getMethods(); // 遍历反射类对象 for (int i = 0; i < method.length; i++) { // 判断获取对象的方法等于读取excel的关键字 if (method[i].getName().trim().equals(Keywords)) { try { // invoke调用关键字方法,字符串参数 method[i].invoke(actionKeyWords, element, value); // Log.info("调用关键字<" + Keywords + ">方法."); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // 如果执行关键字方法回写用例执行步骤excel if (bResult == true) { ExcelUtils.setCellData(Contants.pass, rowNum, Contants.testResult, Contants.excelFile + Contants.excelName, SheetName); } else { ExcelUtils.setCellData(Contants.fail, rowNum, Contants.testResult, Contants.excelFile + Contants.excelName, SheetName); } break; } } } }
关键字的封装,按照excel的用例设计,每个关键字都只需要传入两个值,一个是操作元素,一个是值:function(locotor,value)
使用testng框架开始执行测试用例:
/** * * TODO: 用例演示 * * @author Joe-Tester * @time 2021年9月8日 * @file testExamlpe.java */ public class testExamlpe { // 初始化关键字对象类 public static Object actionKeyWords; /** * 测试用例 * * @throws IOException */ @Test(groups = { "p0" }) void KeywordsTestCase() throws IOException { actionKeyWords = new KeywordsDriven(); StartEngine.StartEngine(actionKeyWords); } }
<?xml version="1.0" encoding="UTF-8"?> <suite name="precodition"> <!--platformName in {web ,android ,ios }--> <!-- <parameter name="className" value="LoginKeyWords"/> --> <test name="Web_Tests"> <groups> <define name="p0"> <include name="p0" /> </define> <run> <include name="p0" /> <!--<exclude name="xxxx" />--> </run> </groups> <classes> <class name="selenium.keyword.testcase.testExamlpe"/> </classes> </test> <listeners> <!-- 添加ExtentRport监听 --> <listener class-name="selenium.keyword.extenreports.MyExtentTestNgListener" /> </listeners> </suite>
// 第一种日志收集器 private static final Logger log = LogManager.getLogger(KeywordsDriven.class.getName()); // 第二种封装log4j /** * * TODO:封装日志工具 * * @author Joe-Tester * @time 2021年9月10日 * @file Log.java */ public class Log { private static Logger Log = Logger.getLogger(Log.class.getName()); // This is to print log for the beginning of the test case, as we usually // run so many test cases as a test suite public static void startTestCase(String sTestCaseName) { Log.info("****************************************************************************************"); Log.info("****************************************************************************************"); Log.info("$$$$$$$$$$$$$$$$$$$$ " + sTestCaseName + " $$$$$$$$$$$$$$$$$$$$"); Log.info("****************************************************************************************"); Log.info("****************************************************************************************"); } // This is to print log for the ending of the test case public static void endTestCase(String sTestCaseName) { Log.info("$$$$$$$$$$$$$$$$$$$$ " + sTestCaseName + " $$$$$$$$$$$$$$$$$$$$"); Log.info("****************************************************************************************\n"); } // Need to create these methods, so that they can be called public static void info(String message) { Log.info(message); } public static void warn(String message) { Log.warn(message); } public static void error(String message) { Log.error(message); } public static void fatal(String message) { Log.fatal(message); } public static void debug(String message) { Log.debug(message); } }
testsuites执行结果:改名叫Scenarios场景,对应一个测试用例
Java版selenium工具实现关键字框架设计,从关键字封装到反射调用关键字、从Excel操作封装到Excel测试用例设计、从测试报告到日志收集器,都一一进行了代码演示;欢迎各位指正!