分享我的第一次Selenium自動化測試框架開發過程
由於公司的開發團隊偏向於使用Java技術,而且公司倡導學習開源技術,所以我選擇用Java語言來進行Selenium WebDriver的自動化框架開發。由於本人沒有Java開發經驗,以前雖然學過QTP但從沒有接觸過Selenium,正好通過這個機會能學習一下自動化測試,同時也學習一下基本的Java開發過程。
一、首先是搭建框架開發環境
按照網上的方法部署eclipse,建立TestAction工程,並Import引用JDK和Selenium-2.44完整包
二、繼續引用和安裝相關jar包
1、首先是要滿足資料驅動(場景用例和動作用例、資料用例都需要放到excel表上),就需要引用jxl.rar包(實現呼叫和操作excel);
2、需要實現自動化框架(有測試套件、測試層)就需要通過eclipse安裝TestNg(網上有相關教程);
三、構建框架的樣例程式碼
1、實現能夠對excel用例資料的呼叫(通過jxl的引用),建立ExcelData.java類檔案(專門用於對excel的呼叫),以下擷取部分程式碼樣例:
/** * @param fileName excel檔名 * @param caseName sheet名 */ public ExcelData(String fileName, String caseName) { super(); this.fileName = fileName; this.caseName = caseName; } /** * 獲得excel表中的資料 */ public Object[][] getExcelData() throws BiffException, IOException { workbook = Workbook.getWorkbook(new File(getPath())); sheet = workbook.getSheet(caseName); rows = sheet.getRows(); columns = sheet.getColumns(); // 為了返回值是Object[][],定義一個多行單列的二維陣列 @SuppressWarnings("unchecked") HashMap<String, String>[][] arrmap = new HashMap[rows - 1][1]; // 對陣列中所有元素hashmap進行初始化 if (rows > 1) { for (int i = 0; i < rows - 1; i++) { arrmap[i][0] = new HashMap<String, String>(); } } else { System.out.println("excel中沒有資料"); } // 獲得首行的列名,作為hashmap的key值 for (int c = 0; c < columns; c++) { String cellvalue = sheet.getCell(c, 0).getContents(); arrkey.add(cellvalue); } // 遍歷所有的單元格的值新增到hashmap中 for (int r = 1; r < rows; r++) { for (int c = 0; c < columns; c++) { String cellvalue = sheet.getCell(c, r).getContents(); arrmap[r - 1][0].put(arrkey.get(c), cellvalue); } } return arrmap; } /** * 獲得excel檔案的路徑 * @return * @throws IOException */ public String getPath() throws IOException { File directory = new File("."); sourceFile = directory.getCanonicalPath() + "\\src\\source\\" + fileName + ".xls"; return sourceFile; }
2、實現對瀏覽器的呼叫,考慮到相容性,需要同時滿足對Chrome、FireFox、IE三大瀏覽器的呼叫,我們需要準備相關驅動chromedriver.exe、IEDriverServer.exe,這兩驅動都是谷歌和IE官方提供的,可以從網上下載到;而FireFox不需要下載驅動,只要安裝瀏覽器就可呼叫(Selenium和FireFox屬於一個團隊開發出來的,待遇就是不一樣)。
有了瀏覽器驅動後(我們把驅動放到工程目錄的WebDriver資料夾下,方便按相對路徑統一呼叫),我們就需要一個能呼叫瀏覽器的類,以下提供核心程式碼樣例:
public static WebDriver getChromeDriver(String url) { //載入Google驅動 //System.setProperty("webdriver.chrome.driver","D:\\java\\chromedriver.exe"); System.setProperty("webdriver.chrome.driver",System.getProperties().getProperty("user.dir")+"\\WebDriver\\chromedriver.exe"); ChromeOptions options = new ChromeOptions(); //通過配置引數禁止data;的出現 options.addArguments("--user-data-dir="+System.getProperties().getProperty("user.home")+"/AppData/Local/Google/Chrome/User Data/Default"); //通過配置引數刪除“您使用的是不受支援的命令列標記:--ignore-certificate-errors。穩定性和安全性會有所下降。”提示 options.addArguments("--start-maximized","allow-running-insecure-content", "--test-type"); WebDriver driver = new ChromeDriver(options); driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); driver.navigate().to(url); return driver; } public static WebDriver getFireFoxDriver(String url){ System.setProperty("webdriver.firefox.bin", "D:\\Program Files\\Mozilla Firefox\\firefox.exe"); // TODO Auto-generated method stub WebDriver driver = new FirefoxDriver(); //Puts a Implicit wait, Will wait for 10 seconds before throwing exception driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); //Launch website driver.navigate().to(url); return driver; } public static WebDriver getIEDriver(String url){ //System.setProperty("webdriver.ie.driver", "D:\\java\\IE64\\IEDriverServer.exe"); System.setProperty("webdriver.ie.driver", System.getProperties().getProperty("user.dir")+"\\WebDriver\\IE32\\IEDriverServer.exe"); DesiredCapabilities capabilities = DesiredCapabilities.internetExplorer(); capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,true); capabilities.setPlatform(Platform.WINDOWS); capabilities.setCapability("silent", true); // TODO Auto-generated method stub WebDriver driver = new InternetExplorerDriver(capabilities); //Puts a Implicit wait, Will wait for 10 seconds before throwing exception driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); //Launch website driver.navigate().to(url); return driver; }
3、寫一個以資料驅動的場景類,來進行單個事務的用例跑測
(1)首行我們需要用TesgNg提供的資料驅動方法(@DataProvider),來獲取一個場景的用例表資料,這個場景從excel的第一個附表獲取
通過action名,調取用例表(用例表是以action名命名的附表),用例表如下所示(ExpectedObject表示用例校驗物件的頁面Element標籤,用;分隔,分號前面的表示ID,分號後面的表示xpath):
以下為用例表資料獲取的程式碼:
@DataProvider(name="action")
public Object[][] Numbers() throws BiffException, IOException{
getActionString = actionData.getActionStr(1);//獲取第一個場景的broswer、url、action名
ExcelData e=new ExcelData("testdata", getActionString.get(2));
return e.getExcelData();
}
然後通過Java的反射機制,實現動態的獲取具體事務類和執行相關操作(每個事務的類名和方法名都與action場景名一致),以下截選相關場景的部分呼叫程式碼:
@Test(dataProvider="action")
public void testAction(HashMap<String, String> data) throws BiffException, IOException {
try {
Class<?> MyClass = Class.forName(packageName+"."+getActionString.get(2));
Method method = MyClass.getMethod(getActionString.get(2),WebDriver.class);
@SuppressWarnings("unused")
String [] results = (String []) method.invoke(,driver);
String ExpObject=data.get("ExpectedObject");
String ExpObject_by=ExpObject.split(";")[0].toString();
String ExpObject_Desc=ExpObject.split(";")[1].toString();
if(ExpObject_by.length()>0){
Assert.assertEquals(driver.findElement(By.id(ExpObject_by)).getText(),data.get("ExpectedData"), getActionString.get(2)+data.get("ID")+"驗證結果:");
}
else if(ExpObject_Desc.length()>0){
Assert.assertEquals(driver.findElement(By.xpath(ExpObject_Desc)).getText(),data.get("ExpectedData"), getActionString.get(2)+data.get("ID")+"驗證結果:");
}
WebDriverDemo.WebSleep(500);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
另外說明的是,呼叫瀏覽器的方法,需要明確是放在@BeforeMethod中,還是在@BeforeClass中,如果是登入校驗測試,就要保證每次執行測試方法都要開啟一次瀏覽器和關閉一次瀏覽器,那麼我們就要把呼叫瀏覽器,和關閉瀏覽器的方法放到@BeforeMethod中和@AfterMethod中。其他業務測試,只要在一個套件類中開啟一次瀏覽器和關閉一次瀏覽器就可以,所以用到的是@BeforeClass和@AfterClass。
4、我們需要再寫一個以動作(關鍵詞)驅動的場景類
同樣,呼叫第二個場景的用例表,樣例程式碼如下:
@DataProvider(name="action")
public Object[][] Numbers() throws BiffException, IOException{
getActionString = actionData.getActionStr(2);//獲取第二個場景的broswer、url、action名
ExcelData e=new ExcelData("testdata", getActionString.get(2));
return e.getExcelData();
}
然後在測試方法中,動態的呼叫具體操作動作,獲取WebElement標籤的方法,包括通過By ID或者By xpath,操作動作以最常見的兩個為例(sendKeys、click),以下為樣例程式碼節選:
@Test(dataProvider="action")
public void testAction(HashMap<String, String> data) throws BiffException, IOException {
//driver.manage().timeouts().implicitlyWait(5,TimeUnit.SECONDS);//找不到element就再給5秒查詢
try {
WebElement TestWebElement = ;
String SetObject=data.get("SetObject").trim();
String SetObject_by=SetObject.split(";")[0].toString();
String SetObject_Desc=SetObject.split(";")[1].toString();
if(SetObject_by.length()>0){
TestWebElement=driver.findElement(By.id(SetObject_by));
}
else if(SetObject_Desc.length()>0){
TestWebElement=driver.findElement(By.xpath(SetObject_Desc));
}
if(data.get("SetOperate").equals("sendKeys")){
TestWebElement.clear();
TestWebElement.sendKeys(data.get("SetValue"));
}else if(data.get("SetOperate").equals("click")){
TestWebElement.click();
}
String ExpObject=data.get("ExpectedObject").trim();
if(ExpObject.length()>0){
String ExpObject_by=ExpObject.split(";")[0].toString();
String ExpObject_Desc=ExpObject.split(";")[1].toString();
if(ExpObject_by.length()>0){
Assert.assertEquals(driver.findElement(By.id(ExpObject_by)).getText(),data.get("ExpectedData"), getActionString.get(2)+data.get("ID")+"驗證結果:");
}
else if(ExpObject_Desc.length()>0){
Assert.assertEquals(driver.findElement(By.xpath(ExpObject_Desc)).getText(),data.get("ExpectedData"), getActionString.get(2)+data.get("ID")+"驗證結果:");
}
}
WebDriverDemo.WebSleep(500);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
這段方法所呼叫的用例表如下所示(以登入為例):
5、剩下就是業務擴充套件類了,所有複雜的事務都可以單獨建立測試類和方法(方便擴充套件維護,只需要在excel場景表中定義後就能呼叫,利用的是Java反射機制),在這裡就不舉例了。
四、實現測試套件呼叫和報告輸出
有了以上步驟,一個可擴充套件的自動化框架已經基本形成,但是還達不到大規模應用測試和指令碼方便可移植,這時候我們引入Ant(可以在Eclipse中安裝外掛,可以直接上網下載後引用),為了能輸出漂亮一點的報告格式,我們還引入一個saxon-8.7.jar。
有了Ant後,我們就可以建議build.xml檔案,就能一鍵bulid我們以上的自動化程式碼,並將執行測試後的結果輸出成報告。
1、首先我們需要編輯好測試套件呼叫的testng.xml,簡單舉例如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="false">
<test verbose="2" name="Test_Action">
<!--<groups>
<run>
<include name="aaa" />
<include name="bbb" />
<include name="ccc" />
</run>
</groups>-->
<classes>
<class name="TestBrowser.ExcActions"/>
<class name="TestBrowser.ExcActions2"/>
</classes>
</test> <!-- Default test -->
</suite> <!-- Default suite -->
2、然後我們需要編輯好一個能引用基礎jar包、build測試程式碼、呼叫testng、輸出漂亮報告的build.xml檔案
<?xml version="1.0" encoding="UTF-8"?>
<project name= "TestAction" basedir= "." default="testoutput"><!--default設定為run表示只執行指令碼,設為testoutput表示執行完指令碼並輸出檢視報告-->
<echo message="import libs" />
<property name= "lib.dir" value= "lib" /> <!--<property name="libdir" location="${basedir}/lib" />-->
<!--<property name="testng.output.dir" location="${basedir}/test-output" />-->
<path id= "test.classpath" >
<!-- adding the saxon jar to your classpath -->
<fileset dir= "${lib.dir}" includes= "*.jar" />
<fileset dir="${basedir}/selenium-2.44.0">
<include name="selenium-java-2.44.0.jar" />
<include name="libs/*.jar" />
</fileset>
</path>
<taskdef name="testng" classname="org.testng.TestNGAntTask" classpathref="test.classpath" />
<target name="clean">
<delete dir="build"/>
</target>
<target name="compile" depends="clean">
<echo message="mkdir"/>
<mkdir dir="build/classes"/>
<javac srcdir="src" destdir="build/classes" debug="on" encoding="UTF-8" includeAntRuntime="false">
<classpath refid="test.classpath"/>
</javac>
</target>
<path id="runpath">
<path refid="test.classpath"/>
<pathelement location="build/classes"/>
</path>
<target name="run" depends="compile">
<testng classpathref="runpath" outputDir="test-output">
<xmlfileset dir="${basedir}" includes="testng.xml"/>
<jvmarg value="-ea" />
</testng>
</target>
<target name= "testoutput" depends="run">
<xslt in= "test-output/testng-results.xml" style= "test-output/testng-results.xsl"
out= "test-output/index1.html" >
<!-- you need to specify the directory here again -->
<param name= "testNgXslt.outputDir" expression= "${basedir}/test-output/" />
<param name="testNgXslt.showRuntimeTotals" expression="true" />
<classpath refid= "test.classpath" />
</xslt>
</target>
</project>
3、完成這些後,我們就可以通過Eclipse直接Run As Ant Build我們的自動化指令碼了,輸出一份還算漂亮的報告:
同時,需要在事務操作類中,對實際結果和預期結果進行比較,並將測試結果寫入excel的用例表中,如下:
String[] result=new String [2];
result[0] = driver.findElement(By.xpath(pars.get(3).split(";")[1].toString())).getText();
result[1] = pars.get(4);
if(result[0].equals(result[1])){//pars.size()-1
ActionsDemo.modifyExcel(Thread.currentThread().getStackTrace()[1].getMethodName(),k,5,"通過");
} else {
ActionsDemo.modifyExcel(Thread.currentThread().getStackTrace()[1].getMethodName(),k,5,"失敗");
}
五、實現自動化框架指令碼的遷移呼叫
以上的指令碼始終是在Eclipse下編譯和呼叫的,如果要實現靈活遷移,隨便換任何一臺只裝了JDK的電腦都能執行,那麼我們就要來點改造
1、首行是保證我們寫的程式碼中,所以需要引用檔案的地方,都用相對路徑的方式,避免程式碼包遷移後需要改路徑。
2、通過批處理呼叫build檔案及用例檔案,呼叫時也是通過批處理自動找到相關路徑,避免用絕對路徑。
3、需要用環境變數的地方,儘量用批處理的方式實現,甚至最好是不用配置環境變數,直接呼叫相引用相對命令檔案的路徑呼叫
以下舉個通過bat批處理呼叫Ant來執行整個框架程式碼的build:
@echo off
::先將測試用例檔案拷到使用者目錄下
copy src\source\testdata.xls %UserProfile%\src\source
%cd%\org.apache.ant_1.9.6\bin\ant.bat -buildfile build.xml
echo 在%cd%\test-output下檢視測試報告
pause
六、進一步實現自動化的持續整合
在以上基礎上,我們還可以通過jenkins實現對自動化指令碼的呼叫,以及達到每日構建,持續整合開發的要求。
1、首先部署jenkins(網上有相關方法),由於本人公司一直在用jenkins,我就省了搭建部署這一步,直接將以上的自動化框架指令碼上傳
2、自動化指令碼完整目錄(包括程式碼、用例、lib、引用的jar、build.xml檔案等)上傳到SVN(再自動從SVN下到jenkins所在伺服器)
3、在jenkins中新建一個測試專案TestAction,主要配置如下:
4、配置完後,就可以立即構建(如果碰到相關報錯問題,就按輸出的提示進行處理),構建成功後,就可以在HTML_Report中看到測試結果:
七、後續處理
到此為止,一個完整的Selenium自動化框架就出來了,要說好用不,不好說,還得經過實踐的檢驗,但是以上這個思考過程和框架的演進過程,應該也是值得借鑑的,畢竟這是我這幾天摸索和學習的過程,對於一個沒有從事過自動化測試,而且沒有做過Java開發的測試人員來說,這只是個開始。
目前來看,這個框架在架構分層上,還是不夠清晰,有很多要改進的東西,從技術上來說,我已經實現了我的目標(學習自動化測試),但是在整體架構和程式碼重構上,還有很多工作沒做,以下貼出一份Selenium自動化框架的分層結構,以便後期按照這個標準進行改進:
測試資料層:獨立封裝資料;
頁面物件層:封裝頁面物件,共頁面任務層做呼叫;
頁面任務層:實現各個獨立頁面的操作;
測試層:實現頁面測試;
測試套件層:實現測試層的管理呼叫;
跟大家推薦一個學習資料分享群:747981058,裡面大牛已經為我們整理好了許多的學習資料,有自動化,介面,效能等等的學習資料!人生是一個逆水行舟的過程,不進則退,咱們一起加油吧!