講到網頁自動測試,一哥地位非 Selenium 莫屬(參考)。雖然 Selenium IDE 支援錄製網頁操作產生測試腳本,但錄製產生的指令常依賴元素的位置順序定位,配置稍有更動立刻破功。依實務經驗,開發者依據設計邏輯手工撰寫還是較可靠的作法,測試指令也較簡潔有效率。Selenium 本身提供好幾種定位器,包含:ID、Name、DOM、XPATH、CSS Selector…等。習慣 jQuery 再回頭使用這些彈性受限的定位器,每每遇到複雜情境,都會先想 jQuery 如何操作,再設法對應成 CSS Selector,但三不五時就會超出 CSS 能力範圍(例如::contains()、:has(),或是結合 closest()、parent() ),心中滿是恨鐵不成鋼的怨氣。

既然 jQuery 好用,我測試的網頁也多半已載入 jQuery,為什麼寫測試時必須將就這堆能力受限的內建定位器呢?最後,我找到一個解法,為 Selenium 寫擴充函式直接支援 jQuery!

我寫了一個user-extensions.js,為 Selenium 類別加入幾個新函式:

  • locateElementByJq: 所有適用 css=… 選擇器的場合,都可以寫成  jq=… 使用 jQuery 選擇器
  • getJqText:支援 verifyJqText/waitForJqText,傳入 jQuery 選擇器取回 .text()
  • getJqVal:支援 verifyJqVal/waitForJqVal,傳入 jQuery 選擇器取回 .val()
  • getJqHtml:支援 verifyJqHtml/waitForJqHtml,傳入 jQuery 選擇器取回 .val()
  • doExecute:Selenium 雖然有 runScript,但執行時 this 指向 Selenium 物件,如要存取 DOM 必須寫 window.document.* 有點囉嗉。改良版的 execute 支援直接輸入 JavaScript,以 window.eval() 執行,就跟在 F12 主控台下指令一樣方便。因此,execute 是為我寫測試最慣用的兵器。
  • getExecute:沿用 execute 概念,可執行複雜 JavaScript 指令自網頁取值,可配合 verifyForExecute、waitForExecute 使用,威力強大!(例如:取得元素內容進行 Regular Expression 比對、檢查元素個數介於 2 到 4 之間… 這類內建指令不易實現的運算比對)

user-extensions.js 的內容如下:

//http://docs.seleniumhq.org/docs/08_user_extensions.jsp
Selenium.prototype.doExecute = function(script) {
    this.browserbot.getCurrentWindow().eval(script);
};
Selenium.prototype.getExecute = function(script) {
    return this.browserbot.getCurrentWindow().eval(script);
};
Selenium.prototype.getJqMethod = function(selector, method) {
    return this.getExecute('$("' + selector + '").' + method + '();');
};
Selenium.prototype.getJqText = function(selector) {
    return this.getJqMethod(selector, "text");
};
Selenium.prototype.getJqHtml = function(selector) {
    return this.getJqMethod(selector, "html");
};
Selenium.prototype.getJqVal = function(selector) {
    return this.getJqMethod(selector, "val");
};
PageBot.prototype.locateElementByJq = function(selector, inDocument) {
    // FF/Chrome/IE9+: defaultView, OldIE: parentWindow
    return (inDocument.defaultView || inDocument.parentWindow)
            .eval("jQuery('" + selector.replace(/'/g, "\\'") + "')[0];");
};

要使用它,只需開啟 Selenium IDE 選項設定,加入 user-extensions.js 路徑。血清注射完成,美國隊長要現身了!

用一個簡單網頁示範:

<!DOCTYPE html>
 
<html>
    <body>
        <div>
            <input type="text" value="Darkthread" />
            <span class='name'>Jeffrey</span>
        </div>
        <div>
            <input type="button" value="Hide" />
            <span>Test</span>
        </div>
        <div class="last">
            <input type="text" value="Selenium" disabled />
            <span style="display:none">Selenium IDE</span>
        </div>
        <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js"></script>
        <script>
            $(":button").click(function() {
                $(".name").hide(3000);
            });
        </script>
    </body>
</html>

如以下圖示,展示使用 jq=div:has(:text) 配合 verifyElementPresent 可以做到 CSS 不支援的 :has() 選擇器;verifyExecute 時應用 JavaScript .match() 做 Regular Expression 比對;另外還展示用 execute 執行 JavaScript 直接對 DOM 動手動腳。

能使用 jQuery 選擇器,又可直接用 JavaScript 操作網頁或進行運算,就能輕鬆在 Selenium 測試案例加入各種自訂邏輯,寫起測試案例再也不會被綁手綁腳囉!


Comments

# by lrd@hust.edu.tw

您好, 因您文章受用良深,請教您一問題如附檔視訊, https://drive.google.com/file/d/1IpDs-O3F9jNniXJT3qfa9dmlRKbDmO2p/view?usp=sharing 環境為FF&SideeX is an extended version of Selenium IDE, CLICK指令可點到輸入欄位之ID,可是相同欄位相同ID,TYPE指令卻是未見任何動作,也就是未自動打字,WHY? 麻煩您了 lrd@hust.edu.tw

# by Jeffrey

to Ird, 只有某個欄位有問題還是全面失效? 改用sendKeys/typeKeys結果會不同嗎?

Post a comment