selenium中有提供pageObject,支持将页面元素和动作单独封装到一个类中。

但是,当页面元素发生变化的时候(在项目的维护过程中,很很容易发生的),就需要去修改源代码。为了解决这个问题,可以实现一套完全解耦的简单测试框架。

该框架的主要思想,是 将各个测试页面的定位信息存放到xml中,解析后的xml信息映射到相应的类中。当页面定位信息改变的时候,只需修改xml文件即可。


下面是项目框架:
base:用来存放一些测试初始化操作以及对selenuim部分功能的2次封装,之后会上代码。

pageLocator:用来存放一个页面定位器的类,该类包含by、value、desc三个属性。方便将页面定位元素的信息映射到我们这个类上。

testCase:测试用例

util:帮助类。存放 如之前博客中封装的getDriver等

下面是该框架的具体实现:

1.先使用maven引入各jar包

selenium、TESTNG(管理用例)、log4j(记录日志)、dom4j(解析xml)、poi(读取excel)。下面是笔者的pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.claire.leafly</groupId><artifactId>projectLemon</artifactId><version>0.0.1-SNAPSHOT</version><dependencies><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>3.4.0</version>
</dependency><!-- https://mvnrepository.com/artifact/org.testng/testng -->
<dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>6.9.10</version><scope>test</scope>
</dependency><!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
<dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version>
</dependency><!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.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></dependencies><build><plugins><!-- 1:解决每次右键项目名-maven->update project 时候,项目jdk版本变了,变回1.5版本或者其他版本2: 解决使用maven编译其他问题:如提示不能在内部类访问外部非final局部变量--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.5.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin></plugins>
</build></project>

2.按照上面项目结构图来创建一个maven工程

3.封装driver的方法,之前的博客中已经详细说明,这里不再赘述。直接上代码

实现一个读取浏览器驱动配置文件的帮助类

package com.demo.auto.claire.util;import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;public class SeleniumUtil {private static Class clazz;private static Object obj ;public static void main(String[] args) {getDriver();}public static WebDriver getDriver() {Document document = null;Element driverNameElement= null;String driverName =null;SAXReader reader = new SAXReader();try {document = reader.read(SeleniumUtil.class.getResourceAsStream("/driverProperties.xml"));} catch (DocumentException e) {e.printStackTrace();}/*** 下面是通过解析XML,获取到驱动的类全名*/Element rootElement = document.getRootElement(); //获取到根节点int index = Integer.parseInt(rootElement.attributeValue("driverIndex"));//获取到根节点上的driverIndex并转成int类型//获取到所有"name"子节点,遍历,找出与根节点中的driverIndex相同的,将其value属性值获取出来,作为类全名用于反射List<Element> driverNameElements = rootElement.elements("name");for (Element driverNameElement1 : driverNameElements) {int i = Integer.parseInt(driverNameElement1.attributeValue("index"));if (i == index) {driverName = driverNameElement1.attributeValue("value");//获取到name子节点的“value”属性值driverNameElement = driverNameElement1;//将该节点赋值给driverElement,后续根据它来获得子节点}}/*** 通过类全名,反射出驱动类来*/try {clazz = Class.forName(driverName);} catch (ClassNotFoundException e) {e.printStackTrace();}/*** 下面是解析XML中的系统参数以及能力参数*/Element propertiesElement = driverNameElement.element("properties");List<Element> propertyElements = propertiesElement.elements("property");//设置系统参数for (Element property : propertyElements) {System.setProperty(property.attributeValue("name"), property.attributeValue("value"));}//设置能力(ie的话,需要设置忽略域设置等级 以及忽略页面百分比的能力)Element capabilitiesElement = driverNameElement.element("capabilities");if (capabilitiesElement != null) {//创建能力对象DesiredCapabilities realCapabilities = new DesiredCapabilities();//获得能力列表List<Element> capabilitiesElements = capabilitiesElement.elements("capability");for (Element capability : capabilitiesElements) {//遍历能力列表,并给能力赋值realCapabilities.setCapability(capability.attributeValue("name"), true);}}/** 通过反射,创建驱动对象*/try {obj = clazz.newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}WebDriver driver = (WebDriver) obj;return driver;}}

下面是驱动器配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!--  只需要修改下面的driverIndex 就可以去创建对应index的驱动-->
<driver driverIndex="0"><!-- 谷歌浏览器配置文件 --><name value="org.openqa.selenium.chrome.ChromeDriver" index="0"><properties><propertyname="ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY"value="E:/driver/chromedriver.exe" /></properties></name><!-- 火狐浏览器 对应的selenium3.x版本 的配置文件 --><name value="org.openqa.selenium.firefox.FirefoxDriver"seleniumVersion="3.x" index="1"><properties><property name="SystemProperty.BROWSER_BINARY"value="C:\Program Files (x86)\Mozilla Firefox\firefox.exe" /><propertyname="GeckoDriverService.GECKO_DRIVER_EXE_PROPERTY"value="E:/driver/geckodriver.exe" /></properties></name><!-- 火狐浏览器 对应的selenium2.x版本 的配置文件 --><name value="org.openqa.selenium.firefox.FirefoxDriver"seleniumVersion="2.x" index="2"><properties><property name="SystemProperty.BROWSER_BINARY"value="C:\Program Files (x86)\Mozilla Firefox\firefox.exe" /></properties></name><!--IE浏览器配置文件 --><name value="org.openqa.selenium.ie.InternetExplorerDriver"index="3"><properties><propertyname="InternetExplorerDriverService.IE_DRIVER_EXE_PROPERTY"value="E:/driver/IEDriverServer.exe" /></properties><capabilities><capabilityname="InternetExplorerDriver.IGNORE_ZOOM_SETTING" /><capabilityname="InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS" /></capabilities></name>
</driver>

4.log4j的配置文件

###根logger设置###
log4j.rootLogger = INFO,console,file### 输出信息到控制台###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.Threshold = info
log4j.appender.console.layout.ConversionPattern = [%p] %d{yyyy-MM-dd HH:mm:ss} method: %l----%m%n###输出INFO 级别以上的日志文件设置###
log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File = E:/log/web.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = info
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} method: %l - [ %p ]----%m%n

5.将测试数据写到excel中,并提供一个读取excel的帮助类

使用poi读取excel。读取出来的数据,可以封装成二维数组object[][]或者iterator(object[]),作为数据提供者,给测试用例使用。

由于二维数组的方式是一次性将所有的测试用例全部读出来的,当测试过程中出现问题或者数据量巨大的时候,再使用这种方式,会造成内存浪费。笔者建议大家使用迭代器的方式来实现。

下面是迭代器的方式(推荐使用):

package com.demo.auto.claire.util;import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;/*** 解析存放到excel中的测试数据,提供给测试case使用。* 读取的excel名称是取测试case的前半部分,如Register_SuccessTester_002,取的是 Register* 所以测试数据的excel取名必须注意!!!* @author cuijing**/
public class ExcelDataProvider implements Iterator<Object[]> {/*** example,测试使用:*/public static void main(String[] args) throws EncryptedDocumentException, InvalidFormatException, IOException {ExcelDataProvider dataProvider = new ExcelDataProvider("002","RegisterTester.xls");while(dataProvider.hasNext()) {dataProvider.next();}}Iterator<Row> itRow;Row currentRow ;    Cell currentCell;String[] cloumnName;/***该构造方法,通过指定的excel和该excel的指定sheet初始化列名数组 初始化行迭代器* @param index*/public ExcelDataProvider(String sheetName,String fileName)  {InputStream inp = ExcelDataProvider.class.getResourceAsStream("/testData/"+fileName+".xls");Workbook workbook =null;try {workbook  = WorkbookFactory.create(inp);} catch (EncryptedDocumentException | InvalidFormatException | IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}Sheet sheet = workbook.getSheet(sheetName);itRow = sheet.iterator();if (itRow.hasNext()) {//获取到第一行currentRow = itRow.next();int length = currentRow.getLastCellNum();cloumnName=new String[length];/*//获取到第一行的每个单元格内容for (int i = 0; i < cloumnName.length; i++) {cloumnName[i] = currentRow.getCell(i+1).getStringCellValue();}*///得到第一行的迭代器Iterator<Cell> cellIt = currentRow.iterator();//将第一行的数据,填充到列名数组中for (int i = 0; cellIt.hasNext(); i++) {currentCell=cellIt.next();cloumnName[i]=currentCell.getStringCellValue();}System.out.println(Arrays.toString(cloumnName));}}/*** 通过行迭代器判断是否还有下一行*/@Overridepublic boolean hasNext() {if (itRow.hasNext()) {return true;}return false;}/*** 通过行迭代器获取到下一行,遍历下一行的所有单元格,将数据放到map中,再将map包装成object的数组*/@Overridepublic Object[] next() {//指向下一行currentRow = itRow.next();//遍历该行的单元格,并将单元格数据填充到map中Map<String, String> map = new LinkedHashMap<>();for (int i = 0; i < cloumnName.length; i++) {currentCell = currentRow.getCell(i, MissingCellPolicy.CREATE_NULL_AS_BLANK);currentCell.setCellType(CellType.STRING);map.put(cloumnName[i], currentCell.getStringCellValue());}Object[] objects = {map};System.out.println(map);return objects;}}

下面是二维数组的方式:

package com.claire.jing.utils;import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;public class ExcelDataProviderPOI {public static void main(String[] args) throws EncryptedDocumentException, InvalidFormatException, IOException {Object[][] objects = getDataProvider("001");}public static Object[][] getDataProvider(String sheetName) throws EncryptedDocumentException, InvalidFormatException, IOException {Object[][] objects =null;InputStream inp = ExcelDataProviderPOI.class.getResourceAsStream("/testData/competitionSearch.xls");Workbook workbook = WorkbookFactory.create(inp);Sheet  sheet = workbook.getSheet(sheetName);int rowNum = sheet.getLastRowNum();objects = new Object[rowNum][];for (int i = 1; i <=rowNum; i++) {int columnNum = sheet.getRow(i).getLastCellNum();System.out.println("第"+i +"行有"+columnNum+"列数据");objects[i-1] = new Object[columnNum];for (int j = 0; j < columnNum; j++) {sheet.getRow(i).getCell(j).setCellType(CellType.STRING);objects[i-1][j] = sheet.getRow(i).getCell(j,MissingCellPolicy.RETURN_NULL_AND_BLANK).getStringCellValue();}}System.out.println(Arrays.deepToString(objects));return objects;}}

6.将页面元素的定位信息写到xml中,并提供相应的解析帮助类,将其映射到指定对象中。

下面是xml文件

<?xml version="1.0" encoding="UTF-8"?>
<pages>
<!-- 代码是通过下面的pageName来定位去读取哪个页面的定位信息
pageName是取测试case的类名前半部分
如Register_SuccessTester_002,取的是 Register--><!-- 注册页面 -->
<page pageName="Register">
<locaters>
<locator by="id" value="mobilephone" des="手机号输入框"></locator>
<locator by="id" value="password" des="密码输入框"></locator>
<locator by="id" value="pwdconfirm" des="确认密码输入框"></locator>
<locator by="id" value="signup-button" des="注册按钮"></locator>
<locator by="className" value="tips" des="提示信息"></locator>
</locaters>
</page><!-- 登录页面 -->
<page pageName="Login">
<locaters>
<locator by="id" value="mobilephone" des="手机号输入框"></locator>
<locator by="id" value="password" des="密码输入框"></locator>
<locator by="id" value="login" des="登录按钮"></locator>
<locator by="id" value="reset" des="重置按钮"></locator>
</locaters>
</page>
</pages>

下面是xml文件的解析帮助类

package com.demo.auto.claire.util;import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import com.demo.auto.claire.pageLocator.Locator;
/*** 1.解析页面定位信息的Url,将解析内容装到map中* <代码是通过下面的pageName来定位去读取哪个页面的定位信息* pageName是取测试case的类名前半部分如Register_SuccessTester_002,取的是 Register* 所以页面定位信息的xml文件 取名必须注意!!!* * 2.将项目中用到的测试页面都写到一个xml中的,所以固定读取的是 /locatorxml/locater.xml* @author cuijing**/public class LocatorUtil {public static void main(String[] args) {Locator locator = getLocator("RegisterTester", "手机号输入框");System.out.println(locator.getBy());}//将页面元素的定位信息存储在map中//通过页面名称,获取到指定页面的所有定位信息//通过定位元素的描述,获取到locator对象private static Map<String,Map<String, Locator>> pageMap = new HashMap<>();//该类第一次被使用,就会去读取指定xml文件(读取页面的所有元素定位信息)static {readXml();}/*** * @param pageName* 页面名称* @param desc* 元素的关键字描述* @return*/public static Locator getLocator(String pageName,String desc) {//获取到指定页面的所有定位信息Map<String, Locator> map = pageMap.get(pageName);//System.out.println("通过页面名称获取到页面所有定位信息"+map);//返回指定描述locator对象Locator locator = map.get(desc);//System.out.println("通过关键字"+desc +"获取到locator对象"+locator);return locator; }/*** 读取xml文件*/public static void readXml() {SAXReader reader = new SAXReader();Document doucment = null;try {doucment = reader.read(LocatorUtil.class.getResourceAsStream("/locatorxml/locater.xml"));} catch (DocumentException e) {System.out.println("读取xml失败");e.printStackTrace();}//获取到根节点pagesElement rootEleent = doucment.getRootElement();//System.out.println("获取到根节点"+rootEleent.getName());//获取到子节点pageList<Element> elements = rootEleent.elements();//System.out.println("获取到"+elements.size()+"个页面");//遍历所有的pagefor (Element element : elements) {String pageName = element.attribute("pageName").getStringValue();//System.out.println("pageName为" +pageName);//获取到page的子节点 locatorsElement locatorsElement = element.element("locaters");//获取到所有的locatorList<Element> locatorElements = locatorsElement.elements();//创建mapMap<String, Locator> LocatorMap = new HashMap<>();for (Element locatorElement : locatorElements) {                Locator locatorObj = new Locator(locatorElement.attributeValue("by"),locatorElement.attributeValue("value"),locatorElement.attributeValue("des"));//System.out.println(locatorObj);//通过描述信息,获取到locator对象
LocatorMap.put(locatorElement.attributeValue("des"), locatorObj);}pageMap.put(pageName, LocatorMap);System.out.println(pageMap);}}
}

解析完成后映射到Locator类:

package com.demo.auto.claire.pageLocator;public class Locator {String by;String value;String desc;//getter setterpublic String getBy() {return by;}public void setBy(String by) {this.by = by;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}//constructionpublic Locator(String by, String value, String desc) {super();this.by = by;this.value = value;this.desc = desc;}//toString@Overridepublic String toString() {return "Locator [by=" + by + ", value=" + value + ", desc=" + desc + "]";}
}

7.BaseTester的设计

在每个测试进行之前,都要启动浏览器驱动--------将启动浏览器放到baseTester中

在每次测试执行之后,都要退出浏览器驱动,并关闭所有的相关窗口------------------将退出驱动放到BaseTester中

使用智能等待的方式获取元素,在获取元素的时候,使用xml文件的解析帮助类提供的方法,获取到Locator类,并通过反射获取到对应的定位元素方法。

可以对一些selenium的简单方法进行二次封装,使得测试脚本更加简洁,后续维护也更加简单。

还可以实现一个检查点技术(其实就是Assert,封装自己需要的assert方法)

下面是笔者部分的BaseTester代码,可以根据需要自行封装。

package com.demo.auto.claire.base;import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;import org.apache.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.DataProvider;import com.demo.auto.claire.pageLocator.Locator;
import com.demo.auto.claire.util.ExcelDataProvider;
import com.demo.auto.claire.util.LocatorUtil;
import com.demo.auto.claire.util.PropertiesUtil;
import com.demo.auto.claire.util.SeleniumUtil;public class BaseTester {public static WebDriver driver;static Map<String, Map<String, Locator>> pagesMap;Logger logger= Logger.getLogger(BaseTester.class);/*** 套件执行前,启动浏览器驱动*/@BeforeSuitepublic void BeforSuit() {driver = SeleniumUtil.getDriver();}/*** 套件执行后,退出当前浏览器驱动,关闭所有相关窗口* @throws InterruptedException*/@AfterSuitepublic void afterSuit() throws InterruptedException {Thread.sleep(5000);driver.quit();}/****************************数据提供者*****************************************/public static void main(String[] args) {String className = "Register_FailTester_001";int firstIndex = className.indexOf("_");int lastIndex = className.lastIndexOf("_");System.out.println(lastIndex);//获取到当前excel的名称(规定excel的名称与我们的类名第一个“_”前字符串相同)String excelName = className.substring(0, firstIndex);System.out.println("当前页面名称为"+excelName);String sheetName =  className.substring(lastIndex+1);System.out.println("当前sheet名称为"+sheetName);}/*** 数据提供者。ExcelDataProvider是通过poi从excel读取的测试数据* @return*/@DataProvider(name="dataProvider")public Iterator<Object[]> dataProvider(){String className = this.getClass().getSimpleName();int firstIndex = className.indexOf("_");int lastIndex = className.lastIndexOf("_");//获取到当前excel的名称(规定excel的名称与我们的类名第一个“_”前字符串相同)String excelName = className.substring(0, firstIndex);//    System.out.println("当前excel名称为"+excelName);String sheetName =  className.substring(lastIndex+1);//    System.out.println("当前sheet名称为"+sheetName);return new ExcelDataProvider(sheetName,excelName);}/*****************************智能等待获取元素*****************************//*** 获取元素* @param timeOut* 超时时间* @param by* 获取元素方法* @return* 返回获取到的元素*/public WebElement getElement(int timeOut,By by) {WebDriverWait wait = new WebDriverWait(driver, timeOut);return wait.until(new ExpectedCondition<WebElement>() {@Overridepublic WebElement apply(WebDriver driver) {return driver.findElement(by);}});}/*** 获取元素,指定等待时间为5S* @param by* 获取元素 方法* @return*/public WebElement getElement(By by) {return getElement(10, by);}/*** 通过关键字定位元素* @param timeOut* 超时时间* @param keyword* 元素关键字,通过该关键字定位到元素* @return*/public WebElement getElement(int timeOut,String keyword) {//通过反射,获取到运行该方法对象的类名//谁继承了该BaseTester类就获取到谁的类名String className = this.getClass().getSimpleName();int index = className.indexOf("_");String pageName = className.substring(0, index);//System.out.println("当前页面名称为"+pageName);WebElement element = getElement(pageName, keyword, timeOut);return element;}/*** 获取指定页面名称和关键字的元素* @param pageName* 页面名称* @param keyword* 元素关键字* @param timeOut* 超时时间* @return* WebElement,返回查找到的元素*/public WebElement getElement(String pageName,String keyword,int timeOut) {//locatorUtil类读取xml,并提供获取Locator的方法,locator对象里装的是by,value,desc//是通过当前运行对象的类名来当做pageName的Locator locator = LocatorUtil.getLocator(pageName, keyword);//获取到定位方式String byStr = locator.getBy();//这个对应到By类中的方法名,如id,name等(8大定位方法)String value = locator.getValue();//对应定位的值,如:By.id(value)WebDriverWait wait = new WebDriverWait(driver, timeOut);return wait.until(new ExpectedCondition<WebElement>() {@Overridepublic WebElement apply(WebDriver driver) {By by = null;//通过反射,获取到By类的字节码文件。Class<By> clazz = By.class;try {//通过反射,获取到指定名称的方法。Method method = clazz.getDeclaredMethod(byStr, String.class);//调用获取到的方法。由于By中的方法是静态的,通过类调用。这里的对象为null。invoke之后返回的是object对象,强制类型转换为By对象by = (By) method.invoke(null, value);}catch (Exception e) {e.printStackTrace();}return driver.findElement(by);}});}/*** 通过关键字定位元素,固定等待时间为5S(获取的是当前页面的元素)* @param keyword* 元素关键字,通过该关键字定位到元素* @return*/public WebElement getElement(String keyword) {return getElement(5, keyword);}/************************selenium简单方法的二次封装***********************************//*** 向指定元素输入内容,对sendKeys方法的二次包装* @param keyWord* 元素关键字,通过该关键字定位到元素* @param context* 要输入的内容*/public void sendkeys(String keyword,String context) {WebElement targetElement = null;try {targetElement = getElement(keyword);targetElement.sendKeys(context);logger.info("成功向元素"+targetElement +"输入内容"+context);} catch (Exception e) {logger.error("向元素"+targetElement +"输入内容"+context+"失败");e.printStackTrace();}}/*** 获取到指定元素,并点击* @param keyword* 元素关键字,通过该关键字定位到元素*/public void click(String keyword) {WebElement targetElement=null;try {targetElement = getElement(keyword);targetElement.click();logger.info("点击元素"+targetElement);} catch (Exception e) {logger.error("点击元素"+targetElement+"失败");e.printStackTrace();}}    /*** 获取指定元素上的文本信息* @param keyword* 元素关键字,通过该关键字定位到元素* @return*/public String getText(String keyword) {WebElement targetElement = null;String text = null;try {targetElement = getElement(keyword);logger.info("获取元素文本信息");text = targetElement.getText();} catch (Exception e) {logger.error("获取元素文本信息失败");e.printStackTrace();}return text;}public void toUrl(String propertiesKey) {String url = PropertiesUtil.getProperties(propertiesKey);driver.get(url);}/**********************************************检查点Assert************************************************************//*** 检查元素文本与预期文本是否相同* @param keyword * 元素关键字,通过该关键字定位到元素* @param expected* 预期文本*/public void assertTextEquals(String keyword,String expected) {WebElement targetElement = getElement(keyword);String actual = targetElement.getText();Assert.assertEquals(actual, expected);}public void assertCanGetPointElement(String pageName,String keyword) {WebElement targetElement = getElement(pageName,keyword,5);Assert.assertFalse(targetElement==null);}/*********************************************************/
}

上述BaseTester类中,封装了一个打开指定url的方法,其中是将url写到了url.properties文件中,并提供了解析帮助类。

url.properties文件

register=http://XXXX.html
login=http://XXXX.html

解析帮助类:

package com.demo.auto.claire.util;import java.io.IOException;
import java.util.Properties;
/*** 该类专门用来处理properties文件* @author cuijing**/
public class PropertiesUtil {public static void main(String[] args) {System.out.println(getProperties("register"));}static Properties properties = new Properties();//只加载一次配置文件static {readURLProperties();}/*** 加载配置文件*/private static void readURLProperties() {try {properties.load(PropertiesUtil.class.getResourceAsStream("/properties/url.properties"));} catch (IOException e) {e.printStackTrace();}}/*** 通过配置文件中的 key获取到value* @param propertiesKey* 配置文件中的key* @return*/public static String getProperties(String propertiesKey) {return properties.getProperty(propertiesKey);}}

5.编写测试cese

编写测试用例的时候,通常都分为正向测试用例和反向测试用例。将登录页面的测试case分为Register_FailTester_001和Register_SuccessTester_002。

每一个测试脚本都继承自BaseTester

注意:该类名的第一个字段Register 必须与测试数据的excel文件和页面定位信息中的pageName一致,第三个字段001或002必须与测试数据的excel中的sheet同名。

方便后期灵活的去读取相应的测试数据和元素定位信息

反向测试脚本:

package com.demo.auto.claire.testcase.register;import java.util.Map;import org.testng.annotations.Test;import com.demo.auto.claire.base.BaseTester;public class Register_FailTester_001 extends BaseTester{/*** 注册失败的用例* @param data* @throws InterruptedException*/@Test(dataProvider="dataProvider")public void test_register_failure_001(Map<String, String> data) throws InterruptedException {//到指定页面去toUrl("register");//向手机号码输入框输入测试数据sendkeys("手机号输入框", data.get("用户名"));sendkeys("密码输入框", data.get("密码"));sendkeys("确认密码输入框", data.get("确认密码"));//点击注册按钮click("注册按钮");Thread.sleep(1000);//断言用例是否成功assertTextEquals("提示信息", data.get("预期结果"));}}

正向测试脚本:

package com.demo.auto.claire.testcase.register;import java.util.Map;import org.testng.annotations.Test;import com.demo.auto.claire.base.BaseTester;public class Register_SuccessTester_002 extends BaseTester{/*** 注册成功的用例* @param data* @throws InterruptedException*/@Test(dataProvider="dataTestersuccess",enabled=false)public void test_register_success_002(Map<String, String> data) throws InterruptedException {toUrl("register");sendkeys("手机号输入框", data.get("用户名"));sendkeys("密码输入框", data.get("密码"));sendkeys("确认密码输入框", data.get("确认密码"));click("注册按钮");//此时会跳转到登录页面Thread.sleep(1000);//等待页面成功跳转assertCanGetPointElement("LoginTester", "登录按钮");}}

可以看到,此时的测试脚本非常的清晰易读。

该框架还未实现截图、发邮件、定时跑的功能。后期更新。。。

自动化测试--实现一套完全解耦的简单测试框架相关推荐

  1. 自动化测试--实现一套完全解耦的简单测试框架(二)

    一:每次运行都需要打开代码工具,如eclipse或者IDE等.为了后面的持续集成,直接使用Maven命令去运行自动化测试,需要引入surfire插件.笔者使用的是2.10版本Surefire和6.9. ...

  2. 【Java 注解】自定义注解 ( 使用注解实现简单测试框架 )

    文章目录 一.定义注解 二.使用注解 三.解析注解 在 [Java 注解]自定义注解 ( 注解属性定义与赋值 ) 博客中讲解了 注解属性 ; 在 [Java 注解]自定义注解 ( 元注解 ) 博客中讲 ...

  3. Java注解案例-简单测试框架

    目录 一.需求 Calculator类: Check注解: TestCheck类: 二.运行结果 控制台: 记录异常的文件(bug.txt): 三.结论 一.需求 需求:当主方法执行后,会自动执行被检 ...

  4. 自动化测试--实现一套完全解耦的测试框架(三)

    之前博客中已经将笔者实现的框架进行过简单介绍,在使用过程中,对以下几点提出优化: 1.页面URL和页面的定位信息保存不同的配置文件中-----------整合到一个配置文件中,相应的配置文件解析做出调 ...

  5. 关于Android的自动化测试,你需要了解的5个测试框架

    Appium Appium是一个开源的移动测试工具,支持iOS和Android,它可以用来测试任何类型的移动应用(原生.网络和混合).作为一个跨平台的工具,你可以在不同的平台上运行相同的测试.为了实现 ...

  6. Java运用注解反射编写简单测试框架

    测试一个计算器类,把异常类型次数其他异常信息自动生成BUG文件 /*** 计算器类*/ public class Calculator {//加法@Checkpublic void add(){Sys ...

  7. 什么是python自动化测试_python已经自动化了,大家一般用什么测试框架?

    首先我们需要明白自动化测试框架更倾向于一种设计思想 ,这种思想指导工具的使用或者自研开发,并且不是只能使用仅仅一种框架,结合被测系统本身特性一般是选择多种测试框架的组合,来满足测试和设计需求(开发.维 ...

  8. 设计自己的基于Selenium 的自动化测试框架-Java版(1) - 为什么selenium还需要测试框架?...

    本人自动化测试接触时间不久,如有误导,概不负责. 既然有了selenium这个开源的自动化的工具,为什么还要我们自己再去写一个框架? Selenium是自动化的工具,当然是可以用在测试领域,但他不是为 ...

  9. 软件自动测试框架,软件自动化测试框架的研究和实现

    摘要: 软件自动化测试是软件工程领域的一项重要课题.随着软件工程理论的不断发展,软件自动化测试在理论上也不断达到新的高度.目前最为成熟的软件自动化测试技术是使用自动测试框架来指导自动化测试的实现.迄今 ...

最新文章

  1. Arduino Yun的主要部件介绍选自Arduino Yun快速入门教程
  2. 54_pytorch GAN(生成对抗网络)、Gan代码示例、WGAN代码示例
  3. [html] 举例说明html的修饰元素有哪些?
  4. linux设置windows共享为yum源
  5. c语言和python的堆栈,python - 在C ++中更快地执行两个程序的可能解释(与Python比较)? - 堆栈内存溢出...
  6. MySQL 驱动的下载方法
  7. ubuntu安装WPS字体缺失的解决办法
  8. 蓝牙调试器-划时代无线调试器
  9. 请求的url中带#是什么意思呢?
  10. 第2次作业——时事点评
  11. Warning: Class ‘com.bupt.dts.DTSFortran‘ not found in module ‘AntlrTest‘
  12. 移动开发必知必会的六大数据统计平台(入门篇)
  13. solr使用shards提示403
  14. Druid连接池实现数据库加密
  15. pandas美国人口分析实例
  16. 【视频分析】大规模机器学习在爱奇艺视频分析理解中的实践
  17. 光敏二极管和光敏三极管的原理、区别、辨别以及应用电路
  18. ubuntu有用的网址
  19. 多线程并发或线程安全问题如何解决?
  20. oracle++dtcol,Oracle 灾备 -- DG 常用SQL

热门文章

  1. c++ 状态模式(state)
  2. silverlight(二)样式
  3. JAVA基础知识(五)数据类型转换
  4. 甭给《程序员》把脉——你不是主编
  5. python 1秒启动一个下载服务器
  6. top.location.href
  7. .htaccess跳转https
  8. mybatis 之 parameterType=Map
  9. Java关键字break、return、continue
  10. CozyRSS2开发记录0-win10开坑