- Selenium自動化測試之道
- Ping++測試團隊
- 3349字
- 2020-11-28 14:42:27
2.4 Selenium WebDriver
如果Selenium IDE已經讓你度過了自動化測試的破冰期,那么接下來要學習的Selenium WebDriver會讓你開闊視野,幫助我們處理更加復雜的測試場景。Selenium WebDriver提供了一套友好的API,支持Java、Python、Ruby和C#等多種編程語言來創(chuàng)建測試腳本。為了突出Selenium WebDriver的設計思想,弱化編程語言本身對測試腳本的影響,本節(jié)將從工作原理入手,在場景演練環(huán)節(jié)分別介紹Python和Java兩門語言的入門示例。
2.4.1 工作原理
Selenium WebDriver是調用瀏覽器的原生接口來操作瀏覽器的。也就是說,測試腳本操作瀏覽器的過程就是在測試腳本中創(chuàng)建WebDriver對象,再通過這個對象調用WebDriver API來訪問瀏覽器接口,從而操作瀏覽器的過程。
如圖2-38所示,我們在測試腳本中使用Selenium WebDriver,無論是哪種平臺、哪種瀏覽器,處理邏輯都是通過一個ComandExecutor發(fā)送命令,實際上就是一條發(fā)送給Web Service的HTTP請求。Web Service是基于特定WebDriver Wire協(xié)議的RESTful接口,測試腳本通知瀏覽器要做的操作都包含于發(fā)送給Web Service的HTTP請求體中。

圖2-38 Selenium WebDriver實現原理
不同瀏覽器的WebDriver子類(FirefoxDriver、InternetExplorerDriver、ChromeDriver、SafariDriver等)都需要依賴特定的瀏覽器原生組件,例如Firefox需要附加組件webdriver.xpi。而IE需要用到一個dll文件來轉化Web Service的命令為瀏覽器調用。
以下是Selenium HttpCommandExecutor類的部分代碼。里面維護了一個Map,它會將簡單的命令轉化為相應的請求URL。當RESTful Web Service接收到HTTP請求后,它便解析出需要執(zhí)行的操作。同時,代碼還表現出:請求是基于sessionId的,這意味著不同WebDriver對象在多線程并行的時候不會有沖突和干擾。
nameToUrl = ImmutableMap.<String, CommandInfo>builder() .put(NEW_SESSION, post("/session")) .put(QUIT, delete("/session/:sessionId")) .put(GET_CURRENT_WINDOW_HANDLE, get("/session/:sessionId/window_handle")) .put(GET_WINDOW_HANDLES, get("/session/:sessionId/window_handles")) .put(GET, post("/session/:sessionId/url")) // The Alert API is still experimental and should not be used. .put(GET_ALERT, get("/session/:sessionId/alert")) .put(DISMISS_ALERT, post("/session/:sessionId/dismiss_alert")) .put(ACCEPT_ALERT, post("/session/:sessionId/accept_alert")) .put(GET_ALERT_TEXT, get("/session/:sessionId/alert_text")) .put(SET_ALERT_VALUE, post("/session/:sessionId/alert_text"))
Selenium WebDriver把這些邏輯都封裝了起來,我們只需要關心driver對象的創(chuàng)建以及調用driver對象的哪個方法來操作頁面元素。
2.4.2 元素定位
元素定位,即通過元素特有的屬性唯一確定元素的過程。比如說,頁面上有標簽、文本框、按鈕等多個元素,我們要編寫腳本來實現按鈕的點擊操作。在編寫Selenium WebDriver腳本之前,首先要了解如何讓瀏覽器“知道”我們將要操作的是哪個元素。
1.常見的元素定位方法
頁面中的元素定位是使用DOM元素屬性進行實現的,使用Firefox瀏覽器的Firebug插件或Chrome瀏覽器的Inspector(檢查器)可以很方便地來定位元素,并查看元素屬性,如圖2-39所示。

圖2-39 使用Chrome Inspector查看元素屬性
下面介紹常見的元素定位方法。注意,Selenium WebDriver腳本是基于WebDriver對象的,這里為了演示元素定位而跳過了WebDriver對象的創(chuàng)建,直接引用了WebDriver對象(driver)。2.4.3場景演練中有WebDriver對象的創(chuàng)建語句,第3章詳細介紹了不同類型的WebDriver對象。
● ID:根據DOM元素的ID屬性定位。例如以下元素及相應定位方法:
<div id="pingxx">...</div> WebElement element = driver.findElement(By.id("pingxx "));
● Name:根據DOM元素的Name屬性定位。
<input name="pingxx" type="text"/> WebElement element = driver.findElement(By.name("pingxx"));
● ClassName:根據DOM元素的Class屬性定位。例如以下元素及相應定位方法:
<div class="test"><span>Pingxx</span></div><div class="test"><span>Gouda</span></div> List<WebElement> tests = driver.findElements(By.className("test"));
● TagName:根據DOM元素的TagName定位。例如以下元素及相應定位方法:
<iframe src="..."></iframe> WebElement frame = driver.findElement(By.tagName("iframe"));
● LinkText:根據DOM元素(link)的文本內容定位。例如以下元素及相應定位方法:
<a href="http://www.google.com/search? q=pingxx">searchPingxx</a> WebElement cheese = driver.findElement(By.linkText("searchPingxx"));
● PartialLinkText:根據DOM元素(link)的部分文本內容定位。例如以下元素及相應定位方法:
<a href="http://www.google.com/search? q=pingxx">search for Pingxx</a> WebElement cheese = driver.findElement(By.partialLinkText("Pingxx"));
● CssSelector:根據CSS選擇器定位元素。
<div id="food"> <span class="dairy">milk</span> <span class="dairy aged">cheese</span> </div> WebElement cheese = driver.findElement(By.cssSelector("#food span.dairy.aged"));
● XPath:使用XPath定位元素。在Firefox瀏覽器中可以下載firebug插件或firepath插件來直接獲得元素的XPath。
<input type="text" name="example" /> <input type="text" name="other" /> List<WebElement> inputs = driver.findElements(By.xpath("http://input"));
2.如何選擇定位方法
前面介紹了多種元素定位的方法,可謂“條條大道通羅馬”。那么,在實際項目中,如何選擇最適合的定位方法呢?這是測試腳本的編寫者經常會面臨的問題。
(1)當頁面元素存在id屬性時,一般使用id來定位,因為id具有唯一性,可以直接定位到這個元素。然而,在實際項目中,有時會出現缺少標準屬性(例如沒有id屬性或者某些元素的id是動態(tài)的)、頁面刷新會改變等情況,這時就只能選擇其他屬性來定位。
(2)如果這個元素不存在諸如id這類唯一值的屬性,也就是說,我們不方便通過某個值直接定位到這個元素。那么,可以轉變思路:先找到一類元素,再通過具體的順序位置定位到某一個元素。一般在這種情況下,可以考慮用TagName或ClassName。當有鏈接需要定位時,可以考慮linkText或partialLinkText方式。代碼里可以直接看到鏈接的文本內容,增強代碼的可讀性,便于維護。
(3)XPath定位很強大,但定位性能不是很好,且可讀性也會變差。例如這一段代碼:
driver.findElement(By.id("btn_login")).click();
上述語句可以很明確地看出是在單擊“登錄”按鈕進行登錄操作,若使用XPath,這句則變成:
driver.findElement(By.xpath(“/html/body/div[2]/div/div/div[3]/a[7]”)).click();
顯然后者的表述形式不夠簡明。另一方面,如果頁面上的元素位置做了調整,即元素路徑變了,那么使用XPath的腳本也要調整,這說明大量使用XPath的測試腳本維護成本很高。因此建議只有在少數元素不好定位的情況下,選擇XPath或cssSelector。
(4)上面提到的均是Selenium WebDriver自帶的元素定位方法,在一些特殊情況下,它們或許都無法滿足我們的需求。但是,我們可以另辟蹊徑,用JavaScript來操作元素。Selenium WebDriver提供了執(zhí)行JavaScript語句的方法,可以直接調用。由于這種方式需要一定的JavaScript功底,筆者這里不做詳細介紹,7.2.2小節(jié)代碼示例中有所涉及。
2.4.3 場景演練
下文將以“Bing搜索”為例介紹Selenium WebDriver編寫測試腳本的整個過程。采用Java和Python兩門語言作為示例代碼。
1.環(huán)境準備
(1)確保編程語言的運行環(huán)境是可用的。
● Java:不論你是否要寫Java代碼,最好都先準備JRE環(huán)境,2.2小節(jié)提到過RemoteWebDriver,它依賴一個啟動Jar包Selenium Server,而在2.4節(jié)中我們也會用到Jar包。訪問http://www.oracle.com/technetwork/java/javase/downloads可下載最新版本的JDK。
● Python:如果你選擇Python作為測試腳本的語言,可訪問https://www.python.org/downloads/下載安裝包。由于Python 2與Python 3不能完全兼容,請選擇合適的Python版本下載。本書的示例代碼均在2.7.8版本中執(zhí)行成功。
(2)下載該編程語言對應的Selenium包,又稱為Selenium Language Binding Package,如圖2-40所示。下載地址:http://docs.seleniumhq.org/download/。如果你使用Python,并且安裝了pip,可使用pip install selenium命令下載。

圖2-40 WebDriver各個語言包的下載頁面
(3)目標瀏覽器以及相應的Driver文件。因瀏覽器的不同,Driver文件的形式和用法也不同。各個Driver對象的介紹將在第3章中詳細說明。下文示例中將使用FireFoxDrvier,無須額外下載Driver文件,只需要Firefox瀏覽器安裝完成即可。
2. Python篇:Selenium WebDriver腳本
測試場景:在http://cn.bing.com/中搜索關鍵字WebDriver。
具體步驟:
步驟 01 在腳本中導入Selenium Python包。
步驟 02 創(chuàng)建Firefox的WebDriver對象。
步驟 03 調用get方法打開Bing頁面。
步驟 04 找到搜索文本框,輸入WebDriver。
步驟 05 單擊“搜索”按鈕。
步驟 06 調用quit方法關閉頁面,結束測試。
from selenium import webdriver driver =webdriver.Firefox() driver.get("http://cn.bing.com/") driver.find_element_by_id("sb_form_q").send_keys("WebDriver") driver.find_element_by_id("sb_form_go").click() driver.quit()
將上述代碼保存為Python文件即可執(zhí)行。這是一個最簡單的示例,既沒有考慮頁面加載所需的等待時間,也沒有考慮輸出測試結果。希望通過這個示例能讓你感受到Selenium WebDriver的入門非常簡單。至于下面的Java篇,是希望讓你了解,對于不同的編程語言,Selenium WebDriver的使用也非常類似。無論你在實際工作中使用的是Python還是Java,抑或是其他語言,本書各個示例都有一定的參考價值。
3. Java篇:Selenium WebDriver腳本
(1)創(chuàng)建測試用例
使用Selenium WebDriver原本與單元測試框架并沒有直接的關系,本節(jié)引入JUnit單元測試框架,可以方便生成報告、進行代碼注解等。
在Eclipse的測試項目中新建一個JUnit Test Case,選擇New JUnit 4 test類型創(chuàng)建,如圖2-41所示。Eclipse一般會自帶JUnit4的Jar包,只需在單擊Finish后彈出的窗口中單擊“確定”按鈕即可。如圖2-42所示,JUnit包已經添加到項目中了。

圖2-41 新建Test Case

圖2-42 測試項目的文件結構
(2)編寫測試腳本
創(chuàng)建成功后就可以在TestBing.java中寫入測試代碼了,代碼如下。這里為了方便,使用了Thread.sleep(2000);作為等待處理,關于Selenium WebDriver的Wait寫法,參見2.4.4小節(jié)。
package com.selenium.test; import org.junit.*; import org.openqa.selenium.*; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class TestBing{ private WebDriver driver; private String baseUrl; @Before public void setUp()throws Exception{ driver=new FirefoxDriver(); baseUrl="http://cn.bing.com/"; } @Test public void testBing()throws Exception{ driver.get(baseUrl); Thread.sleep(2000); driver.findElement(By.id("sb_form_q")).sendKeys("WebDriver"); driver.findElement(By.id("sb_form_go")).click(); } @After public void tearDown()throws Exception{ driver.quit(); } }
(3)運行測試腳本
運行此程序時,需要在TestBing.java上右擊,然后在彈出的快捷菜單中依次單擊Run As→JUnit Test。
以上述代碼為例,若執(zhí)行成功,則會彈出火狐瀏覽器,自動打開必應首頁并輸入關鍵字進行搜索,之后瀏覽器自動退出,測試結束,如圖2-43所示。

圖2-43 腳本的執(zhí)行結果
2.4.4 Wait
在2.3.3小節(jié)Selenese中,我們已經了解了Selenium IDE提供的Wait方法。接下來,將學習Wait在Selenium WebDriver中的寫法。
1.顯式等待
顯式等待(Explicit Wait)就是提供了明確的等待條件,若條件滿足,則不再等待,繼續(xù)執(zhí)行后續(xù)代碼。例如,我們需要清空id屬性為sb_form_q的文本框內容,用Java代碼可以寫為:
driver.findElement(By.id(“sb_form_q”)).clear();
為了避免在頁面加載過程中因sb_form_q元素未出現導致代碼執(zhí)行失敗,我們可以增加Wait條件,等待sb_form_q元素出現之后,再做清空操作。其代碼如下:
WebElement searchElement = (new WebDriverWait(driver,10)).until(new ExpectedCondition <WebElement>(){ public WebElement apply (WebDriver wd){ return wd.findElement(By.id("sb_form_q")); } }); searchElement.clear();
在上述代碼中,時間設置為最多等待10秒,超時會拋出異常TimeoutException。在這10秒內,WebDriverWait默認每隔500毫秒調用一次ExpectedCondition來確認是否找到元素。
2.隱式等待
隱式等待(Implicit Wait)沒有明確設定在何時開始等待,它相當于設置了全局范圍的等待,對所有元素設置等待時間,在WebDriver對象的整個生命周期中生效。若不設置,則默認為0。
代碼如下,在初始化方法中,設置了全局30秒等待時間:
@Before public void setUp() throws Exception { driver = new FirefoxDriver(); baseUrl = "https://www.xxx.com"; driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); }
2.4.5 常用的斷言
表2-1羅列了Selenium WebDriver常用的斷言方法,并附上Selenium IDE中對應的Selenese命令作為對照。
表2-1 Selenium WebDriver常用的斷言方法

(續(xù)表)

- OpenStack Cloud Computing Cookbook(Third Edition)
- Python科學計算(第2版)
- Microsoft Application Virtualization Cookbook
- jQuery從入門到精通 (軟件開發(fā)視頻大講堂)
- 深入淺出DPDK
- Windows內核編程
- 編程與類型系統(tǒng)
- Java SE實踐教程
- Unity&VR游戲美術設計實戰(zhàn)
- 創(chuàng)意UI:Photoshop玩轉APP設計
- Android Studio Cookbook
- Backbone.js Testing
- Practical Predictive Analytics
- PHP Microservices
- 分布式數據庫HBase案例教程