selenium 是用來做瀏覽器測試的工具,讓你可以安排一個流程在真實瀏覽器上運行來測試結果是否如預期,已經被大量用在 Web 的測試上,這已經許多年,也有許多教學了。
而在 selenium 的測試上,最方便的是能跨瀏覽器測試,但是最麻煩的是你要安排有很多瀏覽器的機器(或虛擬機器),並把環境部屬好,供 selenuim 運行。
而 saucelab 是一個雲端的測試平台,就是用來解決這個問題,他提供了隨時供你取用的運算資源,讓你隨時可以模擬各種平台不同版本的作業系統與瀏覽器,他是一個付費的服務,但是有免費試用的額度可以玩玩看。
這篇文章紀錄從頭到尾透過 nodejs 在本機建立建立一個 selenuim 的測試環境,最後在透過 saucelab 來測試其他作業系統與瀏覽器。
需求
Selenium Server 2.44.0.jar:控制介面,可以透過呼叫他來控制隱藏在其背後不同的瀏覽器。
Chrome WebDriver:實際要連結被測試瀏覽器的驅動程式稱為 web driver,每個不同瀏覽器有他自己的 driver 這個範例是用 chrome 為例子。
測試
# with chrome driver $ java -jar selenium-server-standalone-2.44.0.jar -Dwebdriver.chrome.driver=./chromedriver # with ie driver $ java -jar selenium-server-standalone-2.44.0.jar -Dwebdriver.ie.driver=C:\IEDriverServer.exe
啟動 Server 並指定 chrome.webdriver 的路徑,接著挑你想要寫測試的語言,在這邊例子是 nodejs,先安裝相依的套件。
$ npm install selenium-webdriver
跑個簡單的範例,連到 google 搜尋我的網域,測試結果是不是我的網誌。
selenium-webdriver 的文件 不是很好懂,因為他本身帶有流程控制的功能,讓你可以在非同步的 js 也可以照著流程循序執行,來進行測試,但是寫起來就會不太直覺。
因為所有程式執行的當下都沒有做動作,而是像是針對一個 queue 下指令,接著 selenium 就會按照指令一步的執行你的動作,所以要取得當下的狀態都要在 callback 裡面做。
var webdriver = require('selenium-webdriver'); var driver = new webdriver.Builder(). withCapabilities(webdriver.Capabilities.chrome()). build(); driver.get('http://www.google.com.tw'); driver.findElement(webdriver.By.name('q')).sendKeys('mlwmlw.org'); driver.findElement(webdriver.By.name('btnK')).click(); driver.wait(function() { return driver.isElementPresent(webdriver.By.css("h3")).then(function(presented) { return presented; }); }, 3000) driver.findElement(webdriver.By.css("h3")).getText().then(function(text) { if(/阿喵就像家/.test(text)) { console.log('Success'); } else { console.log('Failed'); } }); driver.quit();
如果你要換成透過 saucelab 測試,只要把你的 username 跟 accesskey 設定到環境變數,並且把範例裡面 withCapabilities 換成你想要的瀏覽器與作業系統,工作就會被丟到 saucelab 去執行了,例如:
$ export SAUCE_USERNAME=mlwmlw $ export SAUCE_ACCESS_KEY=growlmwlm
var sauce = 'http://ondemand.saucelabs.com:80/wd/hub'; var driver = new webdriver.Builder(). usingServer(sauce). withCapabilities({ browserName: 'internet explorer', platform: 'Windows 7', version: '8', username: process.env.SAUCE_USERNAME, accessKey: process.env.SAUCE_ACCESS_KEY }). build()
接著去 sauceLab 的工作裡面看,甚至能夠看 live 播出測試的過程,在執行完或失敗時,也可以直接接著操作模擬的介面,saucelab 也有提供純手動的操作可以測試。
IE 醬油
用 iedriver 測試的時候會踩到一些地雷,如果用太多 css selector,根據這篇文章所述,在 IE6,7 因為沒有 querySelector,所以會 selenium 會載入 sizzle 來代替,但在 IE8 以後會用原生的 selector 來實作,聽起來很理想,漸進式的支援,但是..,IE8 那半殘的 selector,沒辦法選擇第幾個元素(nth),所以只能用 xpath 之類的方法繞過了..。
這裡有個 IE8 選元素的方法可以參考: http://automatictester.wordpress.com/2014/05/04/ie8-and-nth-child-css-selector/
例如要選按某個下拉式選單的第 3 個選項好了,從 css 改寫成 xpath
// css selector driver.findElement(webdriver.By.css("select#dropdown option:nth-child(3)")); // xpath selector driver.findElement(webdriver.By.xpath("//select[@id='dropdown']/option[3]"));
同場加映 Phantomjs
如果要在伺服器上測試沒有 GUI 的話,可以用 phantomjs 這個不需要桌面的無頭瀏覽器,只要裝起來他內建就有支援 selenium 的 driver(ghost driver)。只是不像 IE driver 跟 chrome driver 可以榜在 server 上由 server 自動啟動,phantomjs 要自己手動先啟動。
$ npm install phantomjs -g # 跑 hub 模式 $ java -jar selenium-server-standalone-2.44.0.jar -role hub # 另開一個 terminal 啟動 phantomjs 連接到 hub $ phantomjs --webdriver=8080 --webdriver-selenium-grid-hub=http://127.0.0.1:4444
啟動時指定 phantomjs 就能跑哩。
withCapabilities(webdriver.Capabilities.phantomjs())
nightwatch
實際寫了一大堆測試以後,好像只是在叫瀏覽器做事情,程式很雜,很難分類去知道哪個步驟成功哪個步驟失敗,只能最後再從 log 去看結果,程式又不好維護。
所以就找到了可以做單元測試的 nodejs library,nightwatch.js,他可以幫忙把流程切割成好幾個單元,而且提供檢查值的函式,所以可以比較有組織的進行測試,且包覆了一層 selenium 的 api,改善了很多呼叫的介面,例如把等待某個元素這個功能包成一個函式,經常使用到,原本的寫法又笨又長,所以改完以後程式流程變得比較簡潔又易懂。
例如上述的範例改成 nightwatch 就變成如下,是不是簡化蠻多,主要改善應該是比較不需要寫到 callback 了。
module.exports = { "test" : function (client) { client .url("http://www.google.com") .waitForElementVisible("body", 1000) .assert.visible("input[type=text]") .setValue("input[name=q]", "mlwmlw.org") .waitForElementVisible("button[name=btnK]", 1000) .click("button[name=btnK]") .pause(1000) .assert.containsText("h3", "阿喵就像家"); }, "test2": function (client) { client .assert.containsText("h3", "喵喵的筆記") .end(); } };
上述測試的程式放到 tests 資料夾下,並且把設定檔設好,再安裝 nightwatch 以後,讓 nightwatch 去讀 nightwatch.json 指定用 saucelabs 來跑,不然也能指定不同的 environment 來跑在指定的 selenium server 上。
{ "src_folders" : ["tests"], "output_folder" : "tests/reports", "test_settings" : { "default" : { "selenium_host" : "ondemand.saucelabs.com", "username": "${SAUCE_USERNAME}", "access_key": "${SAUCE_ACCESS_KEY}" }, "ie": { "desiredCapabilities": { "browserName": "internet explorer", "browserVersion" : "8.0", "platform": "WINDOWS 7", "javascriptEnabled": true, "acceptSslCerts": true } }, "chrome" : { "desiredCapabilities": { "browserName": "chrome", "platform": "WINDOWS 7", "javascriptEnabled": true, "acceptSslCerts": true } }, "firefox": { "desiredCapabilities": { "browserName": "firefox", "platform": "WINDOWS 7", "javascriptEnabled": true, "acceptSslCerts": true } } } }
$ npm install nightwatch -g $ nightwatch -e ie,chrome,firefox --test=test.js