Page Objects 預先定義一個頁面裡面的元素(Element)、區塊(Section)和命令(Command),意即將一個網頁切分成許多個片段,利用物件把這些片段包裝起來使用,例如:將一個頁面區分為 header、footer 等,然後在測試程式中就用這樣的 header 或 footer 為單位操作 DOM element。
好處有二
♡(´∀`)人(´∀`)♡
本系列文章皆使用這個專案,可以拉下來玩玩;有什麼問題都可以提出 issue。
每一個 Page Object 必須放在獨立的一支檔案,並放在指定資料夾內。
範例檔案結構如下所示,這個有一個專門放 Page Object 的資料夾 page_objects
,裡面有兩支檔案 findPage.js 和 goodsPage.js,它們分別代表了搜尋頁和商品頁的網頁片段。
nightwatch101
├── custom_assertions
├── custom_commands
├── page_objects (放置 Page Object)
| ├── findPage.js (搜尋頁)
| └── goodsPage.js (商品頁)
├── reports
├── screenshots
├── test/e2e
├── globals.js
├── nightwatch.conf.js
├── package.json
└── README.md
Nightwatch Test Runner 必須要知道 Page Objects 的所在位置,因此在 nightwatch.conf.js
的 page_objects_path
設定檔案路徑,可為字串或陣列。
const config = {
"src_folders": [
"test/e2e"
],
"page_objects_path": './page_objects',
},
// ...
}
完整範例可參考這裡。
我們會拿一支檔案來將特定網頁封裝成物件,例如,將露天拍賣的搜尋頁定義成一個 findPage.js 的 Page Objects。將要抓取操作的 DOM element,使用 elements
屬性預先設定好,其中可設定要選取的網頁元素和定位的策略,定位策略可為 CSS Selector(預設)或 Xpath。
module.exports = {
elements: {
searchbox: {
selector: '#search_input' // 要選取的網頁元素
},
submit: {
selector: '//[@name="q"]',
locateStrategy: 'xpath' // 定位策略是 Xpath
}
}
};
引入這個 Page Objects。
require('./page_objects/findPage.js')
接著,測試程式中若要使用這個元素,使用 @
當前綴標記即可。
原先是這樣
browser
.url('http://find.ruten.com.tw/search/s000.php?enc=u&searchfrom=indexbar&k=Pusheen&t=0')
.click('.rt-site-search-submit') // 點擊提交按鈕
改用 Page Objects 就會成這樣
findPage
.navigate()
.click('@submit') // 點擊提交按鈕,但這個按鈕被封裝起來
測試範例如下
範例程式碼。
module.exports = {
'Find Pusheen': function (client) {
var findPage = client.page.findPage();
findPage.navigate()
.assert.title('搜尋結果 : Pusheen - 露天拍賣')
.assert.visible('@searchbox')
.clearValue('@searchbox')
.setValue('@searchbox', 'Pusheen')
.click('@submit');
client.end();
}
};
執行測試,來搜尋關於 Pusheen 的商品。
nightwatch test/e2e/find/findPusheen.js
執行結果。
url
來設定,url 的資料型態可為字串或 function,若使用 function 則可回傳動態網址。navigate
method 來開啟這個頁面。設定靜態網址。
module.exports = {
url: 'http://find.ruten.com.tw/search/s000.php', // 靜態網址
elements: {}
};
設定動態網址。
module.exports = {
url: function() {
return this.api.launchUrl + '/search/s000.php'; // 動態網址
},
elements: {}
};
使用 command 這個屬性來增加指令,這樣封裝的方式讓程式碼更簡單明瞭。Nightwatch 所提供的指令、斷言等,使用 this 即可取得。其中,this.api
/ this.waitForElementVisible
用於在離開 Page Objects 後,繼續使用鍊結呼叫的方式使用 client 底下的其他 API。
這裡撰寫一個負責提交表單的指令「submit」,執行這個指令時,會先等待 1 秒,然後檢視提交按鈕 .rt-site-search-submit
這個 DOM element 是否可見,最後點擊這個按鈕。
定義 Page Objects。
var findCommands = {
submit: function() {
this.api.pause(1000);
return this.waitForElementVisible('@submit', 1000)
.click('@submit')
}
};
module.exports = {
url: 'http://find.ruten.com.tw/search/s000.php?enc=u&searchfrom=indexbar&k=Pusheen&t=0',
commands: [findCommands],
elements: {
searchbox: {
selector: '#search_input'
},
submit: {
selector: '.rt-site-search-submit'
}
}
};
測試程式。
module.exports = {
'Find Pusheen': function (client) {
var findPage = client.page.findPage();
findPage.navigate()
.assert.title('搜尋結果 : Pusheen - 露天拍賣')
.assert.visible('@searchbox')
.clearValue('@searchbox')
.setValue('@searchbox', 'Pusheen')
.submit(); // 客製化指令
client.end();
}
};
執行測試程式。
nightwatch test/e2e/find/findPusheenCustomCommands.js
執行結果。
Page Objects 的使用方式如下
nightwatch.conf.js
的 page_objects_path
中設定檔案路徑,資料型態可為字串或陣列。在優化測試程式碼上,Page Object 可將網頁片段封裝起來成為物件,可重用、增加可讀性,減少維護的複雜度,應多多使用。
備註:若要使用新版的 Page Object(v0.7+),必須使用 elements 或 sections 任一屬性,否則會被視為舊版。
下一篇來看如何使用 Sections 優化 Page Objects。
網誌版。