官網地址:http://www.protractortest.org/
1. 預備環境
protractor 是一個 Node.js 程序,為了運行 protractor ,你首先需要 Node 環境。你還應該檢查一下 Node 的版本,它應該在 v0.10.0 以上。
node --version
Node 中附帶了 npm 包管理工具,通過 npm 可以下載和安裝 protractor。
默認情況下,protractor 使用 Jasmine 作為測試框架。下面的內容將使用 Jasmine 進行, Jasmine 的當前版本是 2.3,我們將使用這個版本。
我們還將使用本地的獨立 Selenium 服務器來控制瀏覽器,你需要安裝 Java Development Kit (JDK) 來運行它,通過執行下面的命令來檢查 java 是否正確安裝。
java -version
2. 安裝 protractor
使用 NPM 進行全局安裝。
npm install -g protractor
這將會安裝兩個工具:protractor
和 webdriver-manager,
先運行一下 protractor --version 確認可以正常工作。
webdriver-manager 用來幫助我們獲取 Selenium Server,但是, 你可能會遇到網絡錯誤。
webdriver-manager update
我這里執行的結果是這樣的。
PS C:\study\demo> webdriver-manager update Updating selenium standalone downloading https://selenium-release.storage.googleapis.com/2.45/selenium-server-standalone-2.45.0.jar... Updating chromedriver downloading https://chromedriver.storage.googleapis.com/2.15/chromedriver_win32.zip... Error: Got error Error: connect ETIMEDOUT 74.125.23.128:443 from https://selenium-release.storage.googleapis.com/2.45/selenium-server-standalone-2.45.0.jar Error: Got error Error: connect ETIMEDOUT 74.125.23.128:443 from https://chromedriver.storage.googleapis.com/2.15/chrome driver_win32.zip
可以看到,實際上需要下載兩個文件:selenium-server-standalone-2.45.0.jar 和 chromedriver_win32.zip ,這兩個文件分別是 selenium 獨立服務器和 chrome 的驅動。
淘寶提供了鏡像地址,我們可以修改一下 webdriver-manager,通過淘寶來下載。
http://npm.taobao.org/mirrors/selenium/
https://npm.taobao.org/mirrors/chromedriver/2.2/
首先找到你的 webdriver-manager 文件,它應該在 C:\Users\your_account\AppData\Roaming\npm\node_modules\protractor\bin 中,找到第 34 行,修改為淘寶的 selenium 地址。
cdn: 'http://npm.taobao.org/mirrors/selenium/', // cdn: 'https://selenium-release.storage.googleapis.com/',
再找到第 46 行,修改為
cdn: 'https://npm.taobao.org/mirrors/chromedriver/', // cdn: 'https://chromedriver.storage.googleapis.com/',
重新執行 webdriver-manager update 就可以下載了。
下載之后,保存到哪里呢?直接執行一下 webdriver-manager,可以看到如下的輸出。
PS C:\study\demo> webdriver-manager Usage: webdriver-manager <command> Commands: update: install or update selected binaries start: start up the selenium server status: list the current available drivers Options: --out_dir Location to output/expect [default: "C:\\Users\\xxx\\AppData\\Roaming\\npm\\node_modules\\protractor\\selenium"] --seleniumPort Optional port for the selenium standalone server --ignore_ssl Ignore SSL certificates [default: false] --proxy Proxy to use for the install or update command --alternate_cdn Alternate CDN to the binaries --standalone Install or update selenium standalone [default: true] --chrome Install or update chromedriver [default: true] --ie Install or update IEDriver [default: false] Please specify one command
這里給出了默認的保存地址。
成功下載之后,啟動服務器檢查一下。
webdriver-manager start
這將會啟動 Selenium 服務器,控制台也會輸出一些日志信息.
PS C:\study\demo> webdriver-manager start seleniumProcess.pid: 4856 15:16:07.179 INFO - Launching a standalone server Setting system property webdriver.chrome.driver to C:\Users\Whao\AppData\Roaming\npm\node_modules\protractor\selenium\chromedriver.exe 15:16:07.238 INFO - Java: Oracle Corporation 23.6-b04 15:16:07.238 INFO - OS: Windows 7 6.1 amd64 15:16:07.245 INFO - v2.45.0, with Core v2.45.0. Built from revision 5017cb8 15:16:07.317 INFO - RemoteWebDriver instances should connect to: http://127.0.0.1:4444/wd/hub 15:16:07.318 INFO - Version Jetty/5.1.x 15:16:07.318 INFO - Started HttpContext[/selenium-server/driver,/selenium-server/driver] 15:16:07.319 INFO - Started HttpContext[/selenium-server,/selenium-server] 15:16:07.319 INFO - Started HttpContext[/,/] 15:16:07.364 INFO - Started org.openqa.jetty.jetty.servlet.ServletHandler@601edf33 15:16:07.364 INFO - Started HttpContext[/wd,/wd] 15:16:07.367 INFO - Started SocketListener on 0.0.0.0:4444 15:16:07.367 INFO - Started org.openqa.jetty.jetty.Server@792bf755
我們的 Protractor 測試將會把測試請求發送到這個服務器,通過它來控制本地的瀏覽器進行測試,在我們的整個教程中,保持這個服務器的運行,在下面的地址,你可以看到關於服務器狀態的信息。
http://localhost:4444/wd/hub
3. 演練
Step 0 write a test
保持上面的命令行窗口運行,重新打開另外一個命令行窗口,創建一個用於測試的干凈文件夾。
Protractor 需要兩個文件來運行測試,一個測試規范文件,一個配置文件。
讓我們從示例的 AngularJS 應用開始寫一個簡單的測試,我們使用位於 http://juliemr.github.io/protractor-demo/ 的超級計算器應用,測試將檢查頁面的 title 是否符合我們的預期。
復制下面的代碼到 spec.js 文件中。
// spec.js describe('Protractor Demo App', function() { it('should have a title', function() { browser.get('http://juliemr.github.io/protractor-demo/'); expect(browser.getTitle()).toEqual('Super Calculator'); }); });
describe 和 it 是 Jasmine 測試框架的語法格式。browser 是通過 protractor 創建的全局變量。它用於瀏覽器范圍的命令控制,比如通過 browser.get 進行導航。
現在,我們創建配置文件,復制一下代碼到 conf.js 中。
// conf.js exports.config = { framework: 'jasmine2', seleniumAddress: 'http://localhost:4444/wd/hub', specs: ['spec.js'] }
配置文件中描述了到何處定位測試規范文件 (specs ),Selenium 服務器的地址 (seleniumAddress), 還說明了我們將會使用 Jasmine 版本 2 作為測試框架。其它配置這里使用默認的配置,Chrome 是默認的瀏覽器。
現在執行測試。
protractor conf.js
你應該看到自動打開了一個 chrome 瀏覽器,然后導航到超級計算器應用地址,然后,瀏覽器自動關閉 (這應該非常快 )。測試的輸出應該是
祝賀你,我們已經運行了第一個 protractor 測試。
Setp 1 - interacting with elements
這一次,我們修改一下測試來與頁面中的元素進行交互,將 spec.js 修改為如下內容。
// spec.js describe('Protractor Demo App', function() { it('should add one and two', function() { browser.get('http://juliemr.github.io/protractor-demo/'); element(by.model('first')).sendKeys(1); element(by.model('second')).sendKeys(2); element(by.id('gobutton')).click(); expect(element(by.binding('latest')).getText()). toEqual('5'); // This is wrong! }); });
這里,我們使用全局的 element 函數和 by 對象,它們也是 protractor 創建的函數,element 函數用來在頁面中定位 HTML 元素,函數返回一個 ElementFinder 對象。可以通過這個對象與頁面元素進行交互或者獲取信息。在這個測試中,我們使用了 sendKeys 在 input 元素中輸入內容,click 函數來模擬點擊按鈕,getText 獲取元素的內容。
element 需要一個參數,一個選擇器參數,用來描述如何找到元素,by 對象用來創建選擇器,這里我們使用了三種類型的選擇器。
- by.model('first') 通過 ng-model='first' 來定位元素。如果你查看 Calculator 頁面的源碼,你會看到
<input type=text ng-model="first">
. - by.id('gobutton') 通過指定的 id 來定位元素,比如 <button id="gobutton"> .
- by.binding('latest') 定位綁定的變量 latest,比如這里的 {{latest}}
執行這個測試。
protractor conf.js
你應該看到頁面中輸入了兩個數字,等待結果顯示出來。由於結果是 3 ,而不是 5,我們的測試失敗。修復這個測試,然后重新運行它。
Step 2 - writeing multiple scenarios
我們將兩個測試放在一起,如下修改 spec.js
// spec.js describe('Protractor Demo App', function() { var firstNumber = element(by.model('first')); var secondNumber = element(by.model('second')); var goButton = element(by.id('gobutton')); var latestResult = element(by.binding('latest')); beforeEach(function() { browser.get('http://juliemr.github.io/protractor-demo/'); }); it('should have a title', function() { expect(browser.getTitle()).toEqual('Super Calculator'); }); it('should add one and two', function() { firstNumber.sendKeys(1); secondNumber.sendKeys(2); goButton.click(); expect(latestResult.getText()).toEqual('3'); }); it('should add four and six', function() { // Fill this in. expect(latestResult.getText()).toEqual('10'); }); });
這里,我們把導航移到了 beforeEach 中,這個函數會在每一個 it 塊之前執行。我們還把 ElementFinder 保存在變量中進行共享使用。
Step 3 - changing the configuration
我們已經寫了一些測試,現在來看看配置文件。在配置文件中,我們可以配置使用什么瀏覽器,如何連接到 Selenium 服務器等等,先改變一下我們使用的服務器。
// conf.js exports.config = { framework: 'jasmine2', seleniumAddress: 'http://localhost:4444/wd/hub', specs: ['spec.js'], capabilities: { browserName: 'firefox' } }
重新運行測試,你會看到測試運行在 firefox 中而不是原來的 chrome。capabilities 對象描述了測試使用的瀏覽器。各種配置參數,參見:the reference config file.
還可以同時在多個瀏覽器上運行測試,例如。
// conf.js exports.config = { framework: 'jasmine2', seleniumAddress: 'http://localhost:4444/wd/hub', specs: ['spec.js'], multiCapabilities: [{ browserName: 'firefox' }, { browserName: 'chrome' }] }
重新運行,你會看到測試運行在 chrome 和 firefox 瀏覽器中。
Step 4 - lists of elements
我們回到測試,把配置文件也調整為僅使用一個瀏覽器。
有的時候,我們需要處理一組元素 ,可以使用 element.all,它會返回 ElementArrayFinder。在 Calculator 中,任何操作都被記錄在 log 中。這是使用 ng-repeat 生成的表格實現的。
我們先進行一系列操作,然后測試它們出現在 log 歷史記錄中,修改 spec.js 如下。
// spec.js describe('Protractor Demo App', function() { var firstNumber = element(by.model('first')); var secondNumber = element(by.model('second')); var goButton = element(by.id('gobutton')); var latestResult = element(by.binding('latest')); var history = element.all(by.repeater('result in memory')); function add(a, b) { firstNumber.sendKeys(a); secondNumber.sendKeys(b); goButton.click(); } beforeEach(function() { browser.get('http://juliemr.github.io/protractor-demo/'); }); it('should have a history', function() { add(1, 2); add(3, 4); expect(history.count()).toEqual(2); add(5, 6); expect(history.count()).toEqual(0); // This is wrong! }); });
我們做了許多事,首先,創建了一個 helper 函數 add。還有一個變量 history。我們通過 by.repeater 使用 element.all 來得到 ElementArrayFinder。在我們的測試中,我們使用 count 方法來斷言期望的長度。修改測試以便通過第二個測試。
除了 count 之外,ElementArrayFinder 還提供了許多方法,讓我們使用 last 來獲得最后一個元素的 ElementFinder ,修改測試。
it('should have a history', function() { add(1, 2); add(3, 4); expect(history.last().getText()).toContain('1 + 2'); expect(history.first().getText()).toContain('foo'); // This is wrong! });
由於 Calculator 在最后報告最早的結果,最早的計算 (1 + 2) 會出現在最后。我們使用 Jasmine 的 toContain 斷言來檢查 "1 + 2" ,元素的文本內容中還包含了時間戳和計算結果。
修復這個測試,正確地期望在第一個歷史記錄中包含了 "3 + 4"。
ElementArrayFinder 還提供了方法 each, map, filter 和 reduce 等等模擬 JavaScript 的數組方法,詳見: Read the API for more details.
4. 參考資料
原文地址:http://www.protractortest.org/#/tutorial