官术网_书友最值得收藏!

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ù)表)

主站蜘蛛池模板: 隆尧县| 赤水市| 岳阳县| 韩城市| 金塔县| 白水县| 绥宁县| 宜宾市| 定安县| 崇义县| 江陵县| 德惠市| 榕江县| 汶上县| 开化县| 高邑县| 芒康县| 兰溪市| 长武县| 武强县| 康乐县| 微山县| 锡林郭勒盟| 合阳县| 呼玛县| 平塘县| 游戏| 高唐县| 沙田区| 上犹县| 石城县| 淄博市| 仙桃市| 光山县| 吉林省| 邹平县| 宁化县| 新安县| 威海市| 丰顺县| 安仁县|