Selenium WebDriver 数据驱动测试框架,以QQ邮箱添加联系人为示例,测试框架结构如下图:
ObjectMap.java
/** * 使用配置文件存储测试页面上的定位和定位表达式,做到定位数据和程序的分离 */ import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; import org.openqa.selenium.By; public class ObjectMap { Properties properties; public ObjectMap(String propFile) { properties = new Properties(); try { FileInputStream in = new FileInputStream(propFile); properties.load(in); in.close(); } catch (IOException e) { System.out.println("读取对象文件出错"); e.printStackTrace(); } } public By getLocator(String ElementNameInpopFile) throws Exception { // 根据变量ElementNameInpopFile,从属性配置文件中读取对应的配置对象 String locator = properties.getProperty(ElementNameInpopFile); // 将配置对象中的定位类型存储到locatorType变量,将定位表达式的值存储到locatorValue变量中 String locatorType = locator.split(":")[0]; String locatorValue = locator.split(":")[1]; // 在Eclipse中的配置文件均默认为ISO-8859-1编码存储,使用getBytes方法可以将字符串编码转换为UTF-8编码,以此来解决在配置文件读取中文乱码的问题 locatorValue = new String(locatorValue.getBytes("ISO-8859-1"), "UTF-8"); // 输出locatorType变量值和locatorValue变量值,验证是否赋值正确 System.out.println("获取的定位类型:" + locatorType + "\t 获取的定位表达式:" + locatorValue); // 根据locatorType的变量值内容判断返回何种定位方式的By对象 if (locatorType.toLowerCase().equals("id")) { return By.id(locatorValue); } else if (locatorType.toLowerCase().equals("name")) { return By.name(locatorValue); } else if ((locatorType.toLowerCase().equals("classname")) || (locatorType.toLowerCase().equals("class"))) { return By.className(locatorValue); } else if ((locatorType.toLowerCase().equals("tagname")) || (locatorType.toLowerCase().equals("tag"))) { return By.className(locatorValue); } else if ((locatorType.toLowerCase().equals("linktext")) || (locatorType.toLowerCase().equals("link"))) { return By.linkText(locatorValue); } else if (locatorType.toLowerCase().equals("partiallinktext")) { return By.partialLinkText(locatorValue); } else if ((locatorType.toLowerCase().equals("cssselector")) || (locatorType.toLowerCase().equals("css"))) { return By.cssSelector(locatorValue); } else if (locatorType.toLowerCase().equals("xpath")) { return By.xpath(locatorValue); } else { throw new Exception("输入的 locator type 未在程序中被定义:" + locatorType); } } }
Constant.java
public class Constant { //测试网址常量 public static final String URL = "http://mail.qq.com"; //测试数据EXCEL路径 public static final String TestDataExcelFilePath = "f:\\QQ邮箱的测试数据.xlsx"; //EXCEL测试数据sheet名称 public static final String TestDataExcelFileSheet = "新建联系人测试用例"; }
ExcelUntil.java
package until; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; public class ExcelUntil { private static XSSFSheet excelWSheet; private static XSSFWorkbook excelWBook; private static XSSFCell cell; private static XSSFRow row; //指定要操作的excel文件的路径及sheet名称 public static void setExcelFile(String path,String sheetName) throws Exception{ FileInputStream excelFile; try { excelFile = new FileInputStream(path); excelWBook = new XSSFWorkbook(excelFile); excelWSheet = excelWBook.getSheet(sheetName); } catch (Exception e) { e.printStackTrace(); } } //读取excel文件指定单元格数据(此方法只针对.xlsx后辍的Excel文件) public static String getCellData(int rowNum,int colNum) throws Exception{ try { //获取指定单元格对象 cell = excelWSheet.getRow(rowNum).getCell(colNum); //获取单元格的内容 //如果为字符串类型,使用getStringCellValue()方法获取单元格内容,如果为数字类型,则用getNumericCellValue()获取单元格内容 String cellData = cell.getStringCellValue(); return cellData; } catch (Exception e) { return ""; } } //在EXCEL的执行单元格中写入数据(此方法只针对.xlsx后辍的Excel文件) rowNum 行号,colNum 列号 public static void setCellData(int rowNum,int colNum,String Result) throws Exception{ try { //获取行对象 row = excelWSheet.getRow(rowNum); //如果单元格为空,则返回null cell = row.getCell(colNum); if(cell == null){ cell=row.createCell(colNum); cell.setCellValue(Result); }else{ cell.setCellValue(Result); } FileOutputStream out = new FileOutputStream(Constant.TestDataExcelFilePath); //将内容写入excel中 excelWBook.write(out); out.flush(); out.close(); } catch (Exception e) { e.printStackTrace(); } } //从EXCEL文件中获取测试数据 public static Object[][] getTestData(String excelFilePath,String sheetName) throws IOException{ //声明一个file文件对象 File file = new File(excelFilePath); //创建一个输入流 FileInputStream in = new FileInputStream(file); //声明workbook对象 Workbook workbook = null; //判断文件扩展名 String fileExtensionName = excelFilePath.substring(excelFilePath.indexOf(".")); if(fileExtensionName.equals(".xlsx")){ workbook = new XSSFWorkbook(in); }else { workbook = new HSSFWorkbook(in); } //获取sheet对象 Sheet sheet = workbook.getSheet(sheetName); //获取sheet中数据的行数,行号从0始 int rowCount = sheet.getLastRowNum()-sheet.getFirstRowNum(); List<Object[]> records = new ArrayList<Object[]>(); //读取数据(省略第一行表头) for(int i=1; i<rowCount+1; i++){ //获取行对象 Row row = sheet.getRow(i); System.out.println(">>>>>>>>>>> "+ row.getLastCellNum()); //声明一个数组存每行的测试数据,excel最后两列不需传值 String[] fields = new String[row.getLastCellNum()-2]; //excel倒数第二列为Y,表示数据行要被测试脚本执行,否则不执行 if(row.getCell(row.getLastCellNum()-2).getStringCellValue().equals("Y")){ for(int j=0; j<row.getLastCellNum()-2; j++){ //判断单元格数据是数字还是字符 //fields[j] = row.getCell(j).getCellTypeEnum() == CellType.STRING ? row.getCell(j).getStringCellValue() : ""+row.getCell(j).getNumericCellValue(); fields[j] = row.getCell(j).getCellType() == CellType.STRING ? row.getCell(j).getStringCellValue() : ""+row.getCell(j).getNumericCellValue(); } records.add(fields); } } //将list转为Object二维数据 Object[][] results = new Object[records.size()][]; //设置二维数据每行的值,每行是一个object对象 for(int i=0; i<records.size(); i++){ results[i]=records.get(i); } return results; } public static int getLastColumnNum(){ //返回数据文件最后一列的列号,如果有12列则返回11 return excelWSheet.getRow(0).getLastCellNum()-1; } }
Log.java
package until; import org.apache.log4j.Logger; public class Log { // 初始化Log4j日志 private static Logger Log = Logger.getLogger(Log.class.getName()); // 打印测试用例开头的日志 public static void startTestCase(String sTestCaseName) { Log.info("------------------ " + sTestCaseName + " " +"开始执行 ------------------"); } //打印测试用例结束的日志 public static void endTestCase(String sTestCaseName) { Log.info("------------------ " + sTestCaseName + " " +"测试执行结束 ---------------"); } 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); } }
LoginPage.java
package pageobject; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import until.ObjectMap; public class LoginPage { private WebElement element = null; //指定页面元素定位表达式配置文件的绝对路径 private ObjectMap objectMap = new ObjectMap("F:\\workspace\\TestNGProj\\ObjectMap.properties"); private WebDriver driver; public LoginPage(WebDriver driver){ this.driver = driver; } //返回登录页面中的用户名输入框页面元素对象 public WebElement username() throws Exception{ element =driver.findElement(objectMap.getLocator("QQ.Email.username")); return element; } //返回登录页面中的密码输入框页面元素对象 public WebElement password() throws Exception { element = driver.findElement(objectMap.getLocator("QQ.Email.password")); return element; } //返回登录页面中的登录按钮页面元素对象 public WebElement login_button() throws Exception { element = driver.findElement(objectMap.getLocator("QQ.Email.login_button")); return element; } }
HomePage.java
package pageobject; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import until.ObjectMap; public class HomePage { private WebElement element = null; private ObjectMap objectMap = new ObjectMap("F:\\workspace\\TestNGProj\\ObjectMap.properties"); private WebDriver driver; public HomePage(WebDriver driver){ this.driver = driver; } //获取登录后主页的“通讯录”链接 public WebElement addressLink() throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.homepage.address_book")); return element; } }
AddressBookPage.java
package pageobject; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import until.ObjectMap; public class AddressBookPage { private WebElement element = null; private ObjectMap objectMap = new ObjectMap("F:\\workspace\\TestNGProj\\ObjectMap.properties"); private WebDriver driver; public AddressBookPage(WebDriver driver){ this.driver = driver; } //获取新建联系人按钮 public WebElement addContactButton() throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.addressBook.add_button")); return element; } //新建联系人页面姓名输入框 public WebElement addContactName()throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.addressBook.add_Contact")); return element; } //新建联系人页面邮件输入框 public WebElement addContactEmail() throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.addressBook.add_email")); return element; } //新建联系人页面电话输入框 public WebElement addContactTell() throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.addressBook.add_tell")); return element; } //新建联系人页面国家输入框 public WebElement addContactCounty() throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.addressBook.add_country")); return element; } //新建联系人页面省份输入框 public WebElement addContactProvince() throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.addressBook.add_province")); return element; } //新建联系人页面城市输入框 public WebElement addContactCity() throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.addressBook.add_city")); return element; } //新建联系人页面保存按钮 public WebElement addContactSaveButton() throws Exception{ element = driver.findElement(objectMap.getLocator("QQEmail.addressBook.save_button")); return element; } }
LoginAction.java
package appmodules; import org.openqa.selenium.WebDriver; import org.testng.annotations.Test; import pageobject.LoginPage; import until.Log; /** * 登录方法的封装,方便其他测试脚本的调用 * */ public class LoginAction { public static void execute(WebDriver driver,String userName,String passWord) throws Exception{ Log.info("访问网址:http://mail.qq.com"); driver.get("http://mail.qq.com"); driver.switchTo().frame("login_frame"); LoginPage loginPage = new LoginPage(driver); loginPage.username().clear(); Log.info("在QQ邮箱登录页面的用户名输入框中输入 "+userName); loginPage.username().sendKeys(userName); Log.info("在QQ邮箱登录页面的密码输入框中输入 "+passWord); loginPage.password().sendKeys(passWord); Log.info("单击登录页面的登录按钮"); loginPage.login_button().click(); //Thread.sleep(5000); } }
AddContactAction.java
package appmodules; import org.openqa.selenium.WebDriver; import pageobject.AddressBookPage; import pageobject.HomePage; import until.Log; public class AddContactAction { public static void execute( WebDriver driver, String userName, String passWord, String contactName, String contactEmail, String contactCountry, String contactProvince, String contactCity) throws Exception{ //调用登录方法 Log.info("调用LoginAction类的execute方法"); LoginAction.execute(driver, userName, passWord); Thread.sleep(3000); HomePage homePage = new HomePage(driver); Log.info("登录后,单击通讯录链接"); homePage.addressLink().click(); driver.switchTo().frame("mainFrame"); AddressBookPage addressBookPage = new AddressBookPage(driver); Log.info("休眠3秒,等待打开通讯录页面"); Thread.sleep(3000); Log.info("在通讯录页面,单击'新增联系人'按钮"); addressBookPage.addContactButton().click(); Log.info("在联系人姓名输入框中,输入: "+contactName); addressBookPage.addContactName().sendKeys(contactName); Log.info("在联系人邮箱输入框中,输入: "+contactEmail); addressBookPage.addContactEmail().sendKeys(contactEmail); //addressBookPage.addContactTell().sendKeys(contactTell); Log.info("在联系人国家输入框中,输入: "+contactCountry); addressBookPage.addContactCounty().sendKeys(contactCountry); Log.info("在联系人省份输入框中,输入: "+contactProvince); addressBookPage.addContactProvince().sendKeys(contactProvince); Log.info("在联系人城市输入框中,输入: "+contactCity); addressBookPage.addContactCity().sendKeys(contactCity); Log.info("单击确定按钮"); addressBookPage.addContactSaveButton().click(); Log.info("休眠5秒,等待保存联系人后返回通讯录的主页面"); Thread.sleep(5000); } }
TestQQEmailAddContact.java
package testscript; import java.io.IOException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import appmodules.AddContactAction; import until.Constant; import until.ExcelUntil; import until.Log; public class TestQQEmailAddContact { public WebDriver driver; //调用Constant类中的常量url private String url = Constant.URL; @DataProvider(name="testData") public static Object[][] data() throws IOException{ //调用ExcelUntil工具类中getTestData()方法获取测试数据 return ExcelUntil.getTestData(Constant.TestDataExcelFilePath, Constant.TestDataExcelFileSheet); } //使用名称为testData的dataProvider作为测试方法的测试数据集 //测试方法一共有12个参数,分别对应Excel数据文件中的1~12列 @Test(dataProvider="testData") public void testAddressBook( String caseRowNumber, String testCaseName, String userName, String passWord, String contactName, String contactEmail, String contactTell, String contactCountry, String contactProvince, String contactCity, String assertContactName, String assertContactEmail ) throws Exception{ Log.startTestCase(testCaseName); driver.get(url); Log.info("调用AddContactAction类的execute方法"); try { AddContactAction.execute(driver, userName, passWord, contactName, contactEmail, contactCountry, contactProvince, contactCity); } catch (AssertionError error) { Log.info("添加联系人失败"); //设置Excel中测试数据行的执行结果为“测试执行失败” ExcelUntil.setCellData(Integer.parseInt(caseRowNumber.split("[.]")[0]), ExcelUntil.getLastColumnNum(), "测试执行失败"); Assert.fail("执行AddContactAction类的execute方法失败"); } Log.info("调用AddContactAction类的execute方法后,休眠3秒钟"); Thread.sleep(3000); Log.info("断言通讯录页面是否包含联系人姓名关键字"); try { Assert.assertTrue(driver.getPageSource().contains(assertContactName)); } catch (AssertionError error) { Log.info("断言通讯录页面是否包含联系人姓名的关键字失败"); ExcelUntil.setCellData(Integer.parseInt(caseRowNumber.split("[.]")[0]), ExcelUntil.getLastColumnNum(), "测试执行失败"); Assert.fail("断言通讯录页面是否包含联系人姓名的关键字失败"); } Log.info("断言通讯录页面是否包含联系人邮箱关键字"); try { Assert.assertTrue(driver.getPageSource().contains(assertContactEmail)); } catch (AssertionError error) { Log.info("断言通讯录页面是否包含联系人邮箱的关键字失败"); ExcelUntil.setCellData(Integer.parseInt(caseRowNumber.split("[.]")[0]), ExcelUntil.getLastColumnNum(), "测试执行失败"); Assert.fail("断言通讯录页面是否包含联系人邮箱的关键字失败"); } Log.info("新建联系人全部断言成功,在Excel的测试数据文件的'测试执行结果'中写入'测试执行成功'"); //断言全部成功,在Excel的测试数据文件的“测试执行结果”中写入“测试执行成功” ExcelUntil.setCellData(Integer.parseInt(caseRowNumber.split("[.]")[0]), ExcelUntil.getLastColumnNum(), "测试执行成功"); Log.info("测试结果成功写入excel数据文件中的测试执行结果列"); Log.endTestCase(testCaseName); } @BeforeMethod public void beforeMethod(){ System.setProperty("webdriver.chrome.driver", "e:\\chromedriver.exe"); driver = new ChromeDriver(); } @AfterMethod public void afterMethod(){ driver.quit(); } @BeforeClass public void BeforeClass() throws Exception{ ExcelUntil.setExcelFile(Constant.TestDataExcelFilePath,Constant.TestDataExcelFileSheet); } }
ObjectMap.properties
QQ邮箱的测试数据.xlsx
个人微信订阅号:专注测试开发、自动化测试。