Excel資料驅動框架實戰
由於該測試框架是我在工作的系統中搭建的,故不寫系統網址,重點記錄搭建的框架過程與重點介紹,方便以後察看。
一、系統介紹:
1)在系統中進行登陸
2)在系統登陸的情況下,進行新增操作
3)在新增的資料中進入詳情介面,新增屬性值
二、資料驅動框架搭建的詳細過程:
1、新建一個java工程,並配置好工程中的WebDriver和TestNG環境,並匯入Excel操作相關和Log4j相關的JAR檔案到工程中。
2、在工程中新建4個Package,分別命名為:
1)cn.oms.modules,用於實現複用的業務邏輯封裝方法。
2)cn.oms.pageobjects,用於實現被測系統的頁面物件。
3)cn.oms.autotest,用於實現具體的測試指令碼邏輯。
4)cn.oms.util,用於實現測試過程中呼叫的工具類方法,例如檔案操作、mapObject、頁面元素的操作方法等。
3、在cn.oms.util的Package下新建ObjectMap類,用於實現在外部配置檔案中配置頁面元素的定位表示式。ObjectMap程式碼如下:
4、在工程中新增一個儲存頁面定位方式和定位表示式的配置檔案objectMap.properties,檔案內容如下:import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; 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 ElementNameInpropFile) throws Exception{ //根據變數ElementNameInpropFile,從屬性配置檔案中讀取對應的配置檔案 String locator=properties.getProperty(ElementNameInpropFile); //將配置物件中的定位型別存到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); } } }
測試頁面中的所有頁面元素的定位方式和定位表示式均可在此檔案中進行定義,實現定位資料和測試程式的分離。在一個配置檔案中修改定位資料,可以在測試指令碼中全域性生效,此方式可以大大提高定位表示式的維護效率。oms.loginPage.username=id>username oms.loginPage.password=id>password oms.loginPage.loginbutton=xpath>//button[@type='submit'] oms.StationManagePage.stationMenu=id>menu_0000000002 oms.StationManagePage.smanageMenu=id>menu_0000000060 oms.StationManagePage.createstationMenu=xpath>//a[@data-target='#modStationModel'] oms.StationManagePage.I_stationName=name>station_name oms.StationManagePage.I_operMerchant=name>oper_merchant_id oms.StationManagePage.I_selectedArea=xpath>//*[@id='city_box'][@ng-show='!!!required']/select[1] oms.StationManagePage.I_selectedCity=xpath>//*[@id='city_box'][@ng-show='!!!required']/select[2] oms.StationManagePage.I_selectedDistrict=xpath>//*[@id='city_box'][@ng-show='!!!required']/select[3] oms.StationManagePage.I_establishTime=name>establish_time oms.StationManagePage.I_saveButton=xpath>//button[@w5c-form-submit='modifyOwnOnlineStation();'] oms.StationManagePage.I_OKButton=xpath>//button[@data-bb-handler='ok'] oms.StationManagePage.I_search=xpath>//input[@type='search'] oms.StationManagePage.I_detailButton=xpath>//a[@class='btn'] oms.StationManagePage.I_locationManageButton=xpath>//a[@ng-click='stationLocationManage();'] oms.StationManagePage.I_newCreateButton=id>addSubmit oms.StationManagePage.I_coordinateX=name>coordinate_x oms.StationManagePage.I_coordinateY=name>coordinate_y oms.StationManagePage.I_locationsaveButton=xpath>//button[@w5c-form-submit='modifyStationLocation();'] oms.StationManagePage.I_locationokButton=xpath>//button[@data-bb-handler='ok'] oms.StationManagePage.I_locationcloseButton=xpath>//*[@id='stationLocationList']/div/div/div[3]/button
5、在cn.oms.pageobjects的Package下新建Login_Page類,用於實現系統登陸頁面的pageobject物件。Login_Page類的程式碼如下:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import cn.oms.util.ObjectMap;
public class Login_Page {
private WebElement element=null;
//指定頁面元素定位表示式配置檔案的絕對檔案路徑
private ObjectMap objectMap=new ObjectMap("E:\\test summary\\DataDrivenFrameWork\\objectMap.properties");
private WebDriver driver;
public Login_Page(WebDriver driver){
this.driver=driver;
}
//返回登入頁面中的使用者名稱輸入框頁面元素物件
public WebElement username() throws Exception{
//使用ObjectMap類中的getLocator方法獲取配置檔案中關於使用者名稱的定位方式和定位表示式
element=driver.findElement(objectMap.getLocator("oms.loginPage.username"));
return element;
}
//返回登入頁面中的密碼輸入框頁面元素物件
public WebElement password() throws Exception{
// 使用ObjectMap類中的getLocator方法獲取配置檔案中關於密碼的定位方式和定位表示式
element = driver.findElement(objectMap.getLocator("oms.loginPage.password"));
return element;
}
//返回登入頁面中的登入按鈕頁面元素物件
public WebElement loginButton() throws Exception{
// 使用ObjectMap類中的getLocator方法獲取配置檔案中關於登入按鈕的定位方式和定位表示式
element = driver.findElement(objectMap.getLocator("oms.loginPage.loginbutton"));
return element;
}
}
6、在cn.oms.modules的Package下新建Login_Action類,將登入的操作邏輯封裝,方便其他測試指令碼進行呼叫,其程式碼如下:
import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import cn.oms.pageobjects.Login_Page;
import cn.oms.util.Constant;
import cn.oms.util.Log;
//由於登入過程是其他測試過程的前提條件,所以將登入的操作邏輯封裝在login_Action類的execute方法中,方便其他測試指令碼進行呼叫
public class Login_Action {
public static void execute(WebDriver driver,String userName,String passWord) throws Exception{
//訪問被測試網站
driver.get(Constant.Url);
//生成一個Login_Page物件例項
Login_Page loginPage=new Login_Page(driver);
//直接使用頁面物件的使用者名稱元素物件,輸入使用者名稱
loginPage.username().sendKeys(userName);
//直接使用頁面物件的密碼元素物件,輸入使用者密碼
loginPage.password().sendKeys(passWord);
//登入
loginPage.loginButton().click();
Thread.sleep(6000);
//斷言登陸後頁面是否包含使用者名稱關鍵字,來驗證是否登入成功
Assert.assertTrue(driver.getPageSource().contains(userName));
Log.info("登入成功");
}
}
7、在cn.oms.autotest中新增LoginTest類,呼叫Login_Action的execute方法,大大減少測試指令碼的重複編寫。其程式碼如下:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import cn.oms.modules.Login_Action;
import cn.oms.util.Constant;
public class LoginTest {
private WebDriver driver;
@Test
public void testLogin() throws Exception {
Login_Action.execute(driver,Constant.username,Constant.password);
}
@BeforeMethod
public void beforeMethod() throws InterruptedException {
//若無法開啟chrome瀏覽器,可設定chrome瀏覽器的安裝路徑
System.setProperty("webdriver.chrome.driver", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe");
driver=new ChromeDriver();
}
@AfterMethod
public void afterMethod() {
//關閉開啟的瀏覽器
driver.quit();
}
}
8、在cn.oms.pageobjects下增加Station_Manage_Page類,並在配置檔案objectMap.properties中補充新增頁面的定位表示式,用於實現系統新增頁面的pageobject物件。Station_Manage_Page類的程式碼如下:
package cn.oms.pageobjects;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import cn.oms.util.ObjectMap;
public class Station_Manage_Page {
private WebElement element=null;
//指定頁面元素定位表示式配置檔案的絕對檔案路徑
private ObjectMap objectMap=new ObjectMap("E:\\test summary\\DataDrivenFrameWork\\objectMap.properties");
private WebDriver driver;
public Station_Manage_Page(WebDriver driver){
this.driver=driver;
}
public WebElement stationMenu() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.stationMenu"));
return element;
}
public WebElement smanageMenu() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.smanageMenu"));
return element;
}
public WebElement createstationMenu() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.createstationMenu"));
return element;
}
public WebElement I_stationName() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_stationName"));
return element;
}
public WebElement I_operMerchant() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_operMerchant"));
return element;
}
//選擇所在城市-省份
public WebElement I_selectedArea() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_selectedArea"));
return element;
}
//選擇所在城市-城市
public WebElement I_selectedCity() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_selectedCity"));
return element;
}
//選擇所在城市-地區
public WebElement I_selectedDistrict() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_selectedDistrict"));
return element;
}
public WebElement I_establishTime() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_establishTime"));
return element;
}
//獲取介面中的“儲存”按鈕
public WebElement I_saveButton() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_saveButton"));
return element;
}
//獲取介面中的“確定”按鈕
public WebElement I_OKButton() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_OKButton"));
return element;
}
//搜尋輸入框
public WebElement I_search() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_search"));
return element;
}
//詳情按鈕
public WebElement I_detailButton() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_detailButton"));
return element;
}
//座標管理按鈕
public WebElement I_locationManageButton() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_locationManageButton"));
return element;
}
//新增按鈕
public WebElement I_newCreateButton() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_newCreateButton"));
return element;
}
//經度
public WebElement I_coordinateX() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_coordinateX"));
return element;
}
//緯度
public WebElement I_coordinateY() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_coordinateY"));
return element;
}
//儲存按鈕
public WebElement I_locationsaveButton() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_locationsaveButton"));
return element;
}
//確定按鈕
public WebElement I_locationokButton() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_locationokButton"));
return element;
}
//關閉按鈕
public WebElement I_locationcloseButton() throws Exception{
element=driver.findElement(objectMap.getLocator("oms.StationManagePage.I_locationcloseButton"));
return element;
}
}
9、在cn.oms.modules下增加StationPage_Action類,具體程式碼:
package cn.oms.modules;
import org.openqa.selenium.WebDriver;
import cn.oms.pageobjects.Station_Manage_Page;
//進入新增頁面
public class StationPage_Action {
public static void execute(WebDriver driver) throws Exception{
Station_Manage_Page stationManagePage=new Station_Manage_Page(driver);
stationManagePage.stationMenu().click();
stationManagePage.smanageMenu().click();
Thread.sleep(3000);
}
}
10、在cn.oms.util下增加Constant類,用於存放常量物件,程式碼如下:
package cn.oms.util;
public class Constant {
//定義測試網址的常量
public static final String Url="http://baidu.com/";
//測試資料路徑
public static final String TestDataExcelFilePath="E:\\test summary\\DataDrivenFrameWork\\omstestdata.xlsx";
//登入使用者名稱
public static final String username="username";
//登入密碼
public static final String password="password";
//測試登入資料檔案sheet名稱
public static final String TestLoginDataExcelFileSheet="logindata";
//測試新增資料檔案sheet名稱
public static final String TestAddStationDataExcelFileSheet="addstationdata";
//測試新增屬性資料檔案sheet名稱
public static final String TestAddLocationDataExcelFileSheet="addlocationdata";
}
如果資料發生了變更,只需要修改該類中的靜態常量值就可以實現修改值在全部測試程式碼中生效,減少了程式碼的維護成本,並且使用常量還增加了測試程式碼的可讀性。
11、加入Log4j的列印日誌功能。在工程的根目錄下,新建名稱為log4j.xml的檔案,具體的檔案內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
<appender name="fileAppender" class="org.apache.log4j.FileAppender">
<param name="Threshold" value="INFO"/>
<param name="File" value="OMSTestLogfile.log"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%c{1}] %m %n"/>
</layout>
</appender>
<root>
<level value="INFO"/>
<appender-ref ref="fileAppender"/>
</root>
</log4j:configuration>
在cn.oms.util下增加Log類,程式碼如下:
package cn.oms.util;
import org.apache.log4j.Logger;
public class Log {
private static Logger Log=Logger.getLogger(Log.class.getName());
//定義測試用例開始執行的列印方法,在日誌中列印測試用例開始執行的資訊
public static void startTestCase(String testCaseName){
Log.info("------------- \""+testCaseName+"\"開始執行 -------------");
}
//定義測試用例執行完畢後的列印方法,在日誌中列印測試用例執行完畢的資訊
public static void endTestCase(String testCaseName){
Log.info("------------- \""+testCaseName+"\"測試執行結束 -------------");
}
//定義列印info級別日誌的方法
public static void info(String message){
Log.info(message);
}
//定義列印error級別日誌的方法
public static void error(String message){
Log.error(message);
}
//定義列印debug級別日誌的方法
public static void debug(String message){
Log.debug(message);
}
}
使用此方法可以將測試程式碼的執行邏輯列印到工程的根目錄下名稱為OMSTestLogfile.log的檔案中。通過日誌資訊,可以檢視測試執行過程中的執行邏輯,日誌檔案可以用於後續測試執行中問題分析和過程監控。
12、新建Excel測試資料檔案omstestdata.xlsx,包含2個sheet,其中Sheet1為addstationdata,Sheet2為addlocationdata,如下:
13、在cn.oms.util下增加ExcelUtil類,用於實現對Excel檔案的讀寫操作,程式碼如下:
package cn.oms.util;
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.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;
import org.apache.poi.ss.usermodel.Row;
//主要實現副檔名為.xlsx的Excel檔案操作
public class ExcelUtil {
private XSSFSheet ExcelWSheet;
private XSSFWorkbook ExcelWBook;
private XSSFCell Cell;
private XSSFRow Row;
//設定要操作Excel的檔案路徑和Excel檔案中的Sheet名稱
//在讀/寫Excel的時候,均需要先呼叫此方法,設定要操作Excel的檔案路徑和Excel檔案中的Sheet名稱
public void setExcelFile(String Path,String SheetName) throws Exception{
FileInputStream ExcelFile;
try{
//例項化Excel檔案的FileInputStream物件
ExcelFile=new FileInputStream(Path);
//例項化Excel檔案的XSSFWorkbook物件
ExcelWBook=new XSSFWorkbook(ExcelFile);
//例項化ExcelWSheet物件,指定Excel檔案的Sheet名稱,後續用於Sheet中行、列和單元格操作
ExcelWSheet=ExcelWBook.getSheet(SheetName);
}catch(Exception e){
throw(e);
}
}
//讀取Excel檔案指定單元格的函式,此函式只支援副檔名為.xlsx的Excel檔案
public String getCellData(int RowNum,int ColNum) throws Exception{
try{
//通過函式引數指定單元格的行號和列號,獲取指定的單元格物件
Cell=ExcelWSheet.getRow(RowNum).getCell(RowNum);
//如果單元格的內容為字串型別,則使用getStringCellValue方法獲取單元格的內容
//如果單元格的內容為數字型別,則使用getNumericCellValue方法獲取單元格的內容
String CellData=(String)(Cell.getCellType()==XSSFCell.CELL_TYPE_STRING
? Cell.getStringCellValue()+""
:Cell.getNumericCellValue());
return CellData;
}catch(Exception e){
return "";
}
}
//在Excel檔案的執行單元格中輸入資料,此函式只支援副檔名為.xlsx的Excel檔案寫入
public void setCellData(int RowNum,int ColNum,String Result) throws Exception{
try{
//獲取Excel檔案中的行物件
Row=ExcelWSheet.getRow(RowNum);
//如果單元格為空,則返回NULL
Cell=Row.getCell(ColNum,Row.RETURN_BLANK_AS_NULL);
if(Cell==null){
//當單元格物件是NULL時,則建立單元格
//如果單元格為空,無法直接呼叫單元格物件的setCellValue方法設定單元格的值
Cell=Row.createCell(ColNum);
//呼叫單元格物件的setCellValue方法設定單元格的值
Cell.setCellValue(Result);
}else{
//單元格有內容,可直接呼叫單元格物件的setCellValue方法設定單元格的值
Cell.setCellValue(Result);
}
//例項化寫入Excel檔案的檔案輸出流物件,相當於儲存
FileOutputStream fileOut=new FileOutputStream(Constant.TestDataExcelFilePath);
//將內容寫入Excel檔案中
ExcelWBook.write(fileOut);
//呼叫flush()方法強制重新整理寫入檔案
fileOut.flush();
//關閉檔案輸出流物件
fileOut.close();
}catch(Exception e){
throw(e);
}
}
//從Excel檔案獲取測試資料的靜態方法
public static Object[][] getTestData(String excelFilePath,String sheetName) throws IOException{
//讀取路徑中的檔案
File file=new File(excelFilePath);
//建立FileInputStream物件用於讀取Excel檔案
FileInputStream inputStream = new FileInputStream(file);
Workbook Workbook = null;
//獲取檔名引數的副檔名,判斷是.xlsx檔案還是.xls檔案
String fileExtensionName = excelFilePath.substring(excelFilePath
.indexOf("."));
if (fileExtensionName.equals(".xlsx")) {
Workbook = new XSSFWorkbook(inputStream);
} else if (fileExtensionName.equals(".xls")) {
Workbook = new HSSFWorkbook(inputStream);
}
//通過sheetName引數,聲稱Sheet物件
Sheet Sheet = Workbook.getSheet(sheetName);
//獲取Excel資料檔案Sheet中資料的行數,getLastRowNum()方法獲取資料的最後一行行號
//getFirstRowNum()方法獲取資料的第一行行號,相減之後得出資料的行數,Excel檔案的行號和列號都是從0開始
int rowCount = Sheet.getLastRowNum() - Sheet.getFirstRowNum();
//建立list物件儲存從Excel資料檔案讀取的資料
List<Object[]> records = new ArrayList<Object[]>();
//迴圈遍歷Excel資料檔案的所有資料,除了第一行,第一行是資料列名稱
for (int i = 1; i < rowCount + 1; i++) {
//使用getShow方法獲取行物件
Row row = Sheet.getRow(i);
/*宣告一個數組,儲存Excel資料檔案每行中的測試用例和資料,陣列大小用getLastCellNum()-2來進行動態宣告。
* 因為Excel中測試資料行的最後一個單元格為測試執行結果,倒數第二個單元格為此測試資料行是否執行的狀態,所以最後2列的資料不需要傳入測試方法中
*/
String fields[] = new String[row.getLastCellNum()-2];
//判斷資料行是否要參與測試執行,標記為 y表示資料行被測試執行,非y則不會執行,會被跳過
if(row.getCell(row.getLastCellNum()-2).getStringCellValue().equals("y")){
for (int j = 0; j < row.getLastCellNum()-2; j++) {
// 使用getCell()和getStringCellValue()方法獲取Excel檔案中的單元格資料
//判斷Excel的單元格欄位是數字還是字元,字串格式用getStringCellValue方法獲取,數字用getNumericCellValue方法獲取
fields[j] = (String)(row.getCell(j).getCellType()==XSSFCell.CELL_TYPE_STRING ?
row.getCell(j).getStringCellValue() :""+ row.getCell(j).getNumericCellValue());
}
// 將fields的資料物件存入records的list中
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 int getLastColumnNum(){
//返回資料檔案最後一列的列號
return ExcelWSheet.getRow(0).getLastCellNum()-1;
}
}
14、在cn.oms.modules下增加QueryStation_Action類,用於查詢資料程式碼的封裝,程式碼如下:
package cn.oms.modules;
import org.openqa.selenium.WebDriver;
import cn.oms.pageobjects.Station_Manage_Page;
public class QueryStation_Action {
public static void execute(WebDriver driver,String I_stationName) throws Exception{
Station_Manage_Page stationManagePage=new Station_Manage_Page(driver);
// 在搜尋框輸入站點名稱
stationManagePage.I_search().clear();
stationManagePage.I_search().sendKeys(I_stationName);
}
}
15、在cn.oms.autotest下增加StationManageTest類,用於實現新增功能,程式碼如下:package cn.oms.autotest;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.xml.DOMConfigurator;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import cn.oms.modules.Login_Action;
import cn.oms.modules.QueryStation_Action;
import cn.oms.modules.StationPage_Action;
import cn.oms.pageobjects.Station_Manage_Page;
import cn.oms.util.Constant;
import cn.oms.util.ExcelUtil;
import cn.oms.util.Log;
public class StationManageTest {
private WebDriver driver;
//設定測試 程式使用的資料檔案和SHEET名稱
ExcelUtil AddStationData=new ExcelUtil();
ExcelUtil AddLocationData=new ExcelUtil();
@DataProvider(name="testData")
public static Object[][] testData() throws IOException{
return ExcelUtil.getTestData(Constant.TestDataExcelFilePath, Constant.TestAddStationDataExcelFileSheet);
}
@Test(dataProvider="testData",priority=1)
public void testAddStation(String CaseRowNumber,String testCaseName,String I_stationName,String I_operMerchant,
String I_selectedArea,String I_selectedCity,String I_selectedDistrict,String I_establishTime) throws Exception{
AddStationData.setExcelFile(Constant.TestDataExcelFilePath, Constant.TestAddStationDataExcelFileSheet);
//傳入變數testCaseName,在日誌中列印測試用例被執行的日誌資訊
Log.startTestCase(testCaseName);
Log.info("新增");
try{
Station_Manage_Page stationManagePage=new Station_Manage_Page(driver);
StationPage_Action.execute(driver);
//點選新增按鈕
stationManagePage.createstationMenu().click();
Thread.sleep(1000);
//新增站介面輸入資訊-名稱
stationManagePage.I_stationName().sendKeys(I_stationName);
//服務商
Select operMerchant=new Select(stationManagePage.I_operMerchant());
operMerchant.selectByVisibleText(I_operMerchant);
//所在城市省份
Select selectedArea=new Select(stationManagePage.I_selectedArea());
selectedArea.selectByVisibleText(I_selectedArea);
//所在城市-選擇城市
Select selectedCity = new Select(stationManagePage.I_selectedCity());
selectedCity.selectByVisibleText(I_selectedCity);
//所在城市-選擇地區
Select selectedDistrict = new Select(stationManagePage.I_selectedDistrict());
selectedDistrict.selectByVisibleText(I_selectedDistrict);
//選擇建立時間--清除日期控制元件是readonly屬性
JavascriptExecutor removeAttribute = (JavascriptExecutor)driver;
//remove readonly attribute
removeAttribute.executeScript("var setDate=document.getElementsByName(\"establish_time\")[0];setDate.removeAttribute('readonly');") ;
stationManagePage.I_establishTime().sendKeys(I_establishTime);
// 點選儲存按鈕
stationManagePage.I_saveButton().click();
Thread.sleep(1000);
//點選確定按鈕
stationManagePage.I_OKButton().click();
}catch(AssertionError error){
Log.info("新增失敗");
/*執行AddStation_Action的execute方法失敗時,catch語句可以捕獲AssertionError型別異常,並設定Excel中測試資料行的執行結果為“測試執行失敗”.
* 由於Excel中的序號格式被預設設定為帶有一位小數,所以用split("[.]")[0]語句獲取序號的整數部分,並傳給setCellData函式在對應序號的測試資料行的最後
* 一列設定“測試執行失敗”.
*/
AddStationData.setCellData(Integer.parseInt(CaseRowNumber.split("[.]")[0]), AddStationData.getLastColumnNum(), "測試執行失敗");
//呼叫Assert.fail方法將此測試用例設定為執行失敗,後續測試程式碼將不被執行
Assert.fail("新增失敗");
}
Log.info("新增後,等待3秒");
Thread.sleep(3000);
QueryStation_Action.execute(driver, I_stationName);//輸入名稱進行查詢
Thread.sleep(3000);
Log.info("斷言介面是否包含新增名稱的關鍵字");
try{
Assert.assertTrue(driver.getPageSource().contains(I_stationName));
}catch(AssertionError error){
Log.info("斷言管理介面是否包含新增名稱關鍵字失敗");
AddStationData.setCellData(Integer.parseInt(CaseRowNumber.split("[.]")[0]), AddStationData.getLastColumnNum(), "測試執行失敗");
//呼叫Assert.fail方法將此測試用例設定為執行失敗,後續測試程式碼將不被執行
Assert.fail("斷言管理介面是否包含新增名稱的關鍵字失敗");
}
Log.info("新增全部斷言成功,在Excel的測試資料檔案的“測試執行結果”列中寫入“執行成功”");
AddStationData.setCellData(Integer.parseInt(CaseRowNumber.split("[.]")[0]), AddStationData.getLastColumnNum(), "測試成功");
Log.info("測試結果寫入Excel資料檔案的“測試執行結果”列");
Log.endTestCase(testCaseName);
}
@DataProvider(name="addLocationData")
public static Object[][] LocationData() throws IOException {
return ExcelUtil.getTestData(Constant.TestDataExcelFilePath, Constant.TestAddLocationDataExcelFileSheet);
}
@Test(dataProvider="addLocationData",priority=2)
public void testAddLocation(String CaseRowNumber,String testCaseName,String I_stationName,String I_coordinateX,String I_coordinateY)
throws Exception{
AddLocationData.setExcelFile(Constant.TestDataExcelFilePath, Constant.TestAddLocationDataExcelFileSheet);
//傳入變數testCaseName,在日誌中列印測試用例被執行的日誌資訊
Log.startTestCase(testCaseName);
Station_Manage_Page stationManagePage=new Station_Manage_Page(driver);
StationPage_Action.execute(driver);
QueryStation_Action.execute(driver, I_stationName);//輸入名稱進行查詢
Thread.sleep(1000);
stationManagePage.I_detailButton().click();
Thread.sleep(2000);
try{
stationManagePage.I_locationManageButton().click();
Thread.sleep(2000);
stationManagePage.I_newCreateButton().click();
stationManagePage.I_coordinateX().sendKeys(I_coordinateX);
stationManagePage.I_coordinateY().sendKeys(I_coordinateY);
stationManagePage.I_locationsaveButton().click();
stationManagePage.I_locationokButton().click();
}catch(AssertionError error){
Log.info("新增座標失敗");
AddLocationData.setCellData(Integer.parseInt(CaseRowNumber.split("[.]")[0]), AddLocationData.getLastColumnNum(), "測試執行失敗");
//呼叫Assert.fail方法將此測試用例設定為執行失敗,後續測試程式碼將不被執行
Assert.fail("新增座標失敗");
}
Thread.sleep(3000);
Log.info("斷言座標介面是否包含新增經度的關鍵字");
try{
Assert.assertTrue(driver.getPageSource().contains(I_coordinateX));
}catch(AssertionError error){
Log.info("斷言管理介面是否包含新增經度關鍵字失敗");
AddLocationData.setCellData(Integer.parseInt(CaseRowNumber.split("[.]")[0]), AddLocationData.getLastColumnNum(), "測試執行失敗");
//呼叫Assert.fail方法將此測試用例設定為執行失敗,後續測試程式碼將不被執行
Assert.fail("斷言管理介面是否包含新增經度的關鍵字失敗");
}
Log.info("斷言管理介面是否包含新增緯度的關鍵字");
try{
Assert.assertTrue(driver.getPageSource().contains(I_coordinateY));
}catch(AssertionError error){
Log.info("斷言管理介面是否包含新增緯度關鍵字失敗");
AddLocationData.setCellData(Integer.parseInt(CaseRowNumber.split("[.]")[0]), AddLocationData.getLastColumnNum(), "測試執行失敗");
//呼叫Assert.fail方法將此測試用例設定為執行失敗,後續測試程式碼將不被執行
Assert.fail("斷言管理介面是否包含新增緯度的關鍵字失敗");
}
Log.info("新增全部斷言成功,在Excel的測試資料檔案的“測試執行結果”列中寫入“執行成功”");
AddLocationData.setCellData(Integer.parseInt(CaseRowNumber.split("[.]")[0]), AddLocationData.getLastColumnNum(), "測試成功");
Log.info("測試結果寫入Excel資料檔案的“測試執行結果”列");
stationManagePage.I_locationcloseButton().click();
Log.endTestCase(testCaseName);
}
@BeforeClass
public void BeforeClass() throws Exception{
DOMConfigurator.configure("log4j.xml");
//若無法開啟chrome瀏覽器,可設定chrome瀏覽器的安裝路徑
System.setProperty("webdriver.chrome.driver", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe");
driver=new ChromeDriver();
//設定等待時間為5秒
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
Login_Action.execute(driver,Constant.username,Constant.password);
}
@AfterClass
public void afterTest() {
//關閉開啟的瀏覽器
driver.quit();
}
}
在Eclipse工程中,所有Package和類的結構如下:
三、資料測試驅動框架的優點分析:
1、通過配置檔案,實現頁面元素定位表示式和測試程式碼的分離。
2、使用ObjectMap方式 ,簡化頁面元素定位相關的程式碼工作量。
3、使用PageObject模式,封裝了系統中的頁面元素,方便測試程式碼呼叫,也實現了一處維護全域性生效的目標。
4、在cn.oms.modules的Package中封裝了常用的頁面物件操作方法,簡化了測試指令碼編寫的工作量。
5、在Excel檔案中定義多個測試資料,測試框架可自動呼叫測試資料完成資料驅動測試。
6、實現了測試執行過程中的日誌記錄功能,可以通過日誌檔案分析測試指令碼執行的情況。
7、在Excel檔案測試資料行中,通過設定“測試資料是否執行”列的內容為y或者n,可自定義選擇測試資料,測試執行結束後會在“測試結果”列中顯示測試執行的結果,方便測試人員檢視。
四、總結:
這是我第一個自己搭建的自動化框架,過程中也查看了很多資料,有以下幾個感悟:
個人覺得那個頁面元素和測試程式碼分離在我測試的系統中不是很必要,這個可以應用在頁面元素比較多的情況,我所測系統的頁面元素不是很多,使用FindBy註解就可以了。
在StationManageTest類中有2個不同的案例,用到的資料不一樣,我使用了TestNG的資料驅動註解進行資料驅動測試,但是這個方法必須把一個案例中所有資料都測試完成了才能進行下一個案例測試,其實我2個案例是有關聯的,還需要繼續研究如何實現2個測試類中資料一行行的執行。
該框架中缺少截圖功能,需要新增。
在我剛進入自動化測試工作時,用過基於資料庫和網頁搭建的框架。通過這個框架,我覺得可以借鑑此框架的思想實現基於資料庫和網頁框架的資料驅動框架。藉助Web方式,通過瀏覽器來進行資料驅動測試,完成測試資料的定義、測試用例的執行和測試結果的檢視。