Nightwatch101 #11:BDD Assert
21 Dec 2017Nightwatch 斷言庫(Assertion Library)除了提供上一篇提到的 Expect 外,還有 Assert 和 Verify。在 Nightwatch 中,.assert
和 .verify
的 library 基本上是做相同的事情,差別只在於斷言失敗時的處理方式。
.assert
:斷言失敗時,測試結束,忽略剩餘未執行的部份。.verify
:斷言失敗時,會把測試失敗記綠下來,然後繼續剩餘未執行的部份。
像是這樣…
.assert
:斷言失敗時,測試結束,忽略剩餘未執行的部份。
.verify
:斷言失敗時,會把測試失敗記綠下來,然後繼續剩餘未執行的部份。
本篇先來看 Assert。
♡(´∀`)人(´∀`)♡
本系列文章皆使用這個專案,可以拉下來玩玩;有什麼問題都可以提出 issue。
語法介紹
.attributeContains()
檢視屬性(attribute)是否 包含 指定的值,若否則顯示客製化訊息(optional)。
例如:檢視 .link
這個 DOM element 的屬性 href 的值是否包含 sample.com
,若否則顯示客製化訊息「連結錯誤」。
browser.assert.attributeContains('.link', 'href', 'sample.com', '連結錯誤');
回顧 Expect 的語法。
browser.expect.element('.link').to.have.attribute('href').which.contains('sample.com');
.attributeEquals()
檢視屬性(attribute)是否 等於 指定的值,若否則顯示客製化訊息(optional)。
例如:檢視 .link
這個 DOM element 的屬性 href 的值是否為 sample.com
,若否則顯示客製化訊息「連結錯誤」。
browser.assert.attributeEquals('.link', 'href', 'sample.com', '連結錯誤');
回顧 Expect 的語法。
browser.expect.element('.link').to.have.attribute('href').equals('sample.com');
.containsText()
檢視 DOM element 的 inner text 是否包含某字串,若否則顯示客製化訊息(optional)。
例如:檢視 #text
這個 DOM element 的 inner text 的值是否為 Hello World!
。
browser.assert.containsText('#text', 'Hello World!');
回顧 Expect 的語法。
browser.expect.element('#text').text.to.contain('Hello World!');
.cssClassPresent()
檢視 DOM element 是否含有某個 CSS class,若否則顯示客製化訊息(optional)。
例如:檢視 #wrap
這個 DOM element 的 CSS class 是否含有 .container
這個 class,若否則顯示客製化錯誤訊息「使用的 class 有誤」。
browser.assert.cssClassPresent('#wrap', 'container', '使用的 class 有誤');
回顧 Expect 的語法。
browser.expect.element('#wrap').to.have.attribute('class').which.contains('container');
.cssClassNotPresent()
檢視 DOM element 是否沒有某個 CSS class,與 cssClassPresent 相反。
例如:檢視 #wrap
這個 DOM element 的 CSS class 不是否含有 .container
這個 class。
browser.assert.cssClassNotPresent('#wrap', 'container');
回顧 Expect 的語法。
browser.expect.element('#wrap').not.to.have.attribute('class').which.contains('container');
.cssProperty()
檢視 DOM element 的 CSS 屬性是否含有某個值,若否則顯示客製化訊息(optional)。
例如:檢視 #text
這個 DOM element 的 CSS 屬性 display
的值是否為 block
。
browser.assert.cssProperty('#text', 'display', 'block');
回顧 Expect 的語法。
browser.expect.element('#text').to.have.css('display').which.equals('block');
.elementPresent()
檢視 DOM element 是否存在,若否則顯示客製化訊息(optional)。
例如:檢視 #text
這個 DOM element 是否存在。
browser.assert.elementPresent('#text');
回顧 Expect 的語法。
browser.expect.element('#text').to.be.present;
.elementNotPresent()
檢視 DOM element 是否不存在,與 elementPresent 相反。
例如:檢視 #text
這個 DOM element 是否不存在。
browser.assert.elementNotPresent('#text');
回顧 Expect 的語法。
browser.expect.element('#text').to.not.be.present;
.visible()
檢視 DOM element 是否可見,若否則顯示客製化訊息(optional)。與 hidden
相反。
例如:檢視 .this_element_should_be_visible
這個 DOM element 是否存在。
browser.assert.visible('.this_element_should_be_visible');
回顧 Expect 的語法。
browser.expect.element('.this_element_should_be_visible').to.be.visible;
.hidden()
檢視 DOM element 是否隱藏(即不可見),若否則顯示客製化訊息(optional)。與 visible
相反。
例如:檢視 .this_element_hould_not_be_visible
這個 DOM element 是否隱藏。
browser.assert.hidden('.this_element_hould_not_be_visible');
回顧 Expect 的語法。
browser.expect.element('.this_element_should_be_visible').not.to.be.visible;
.title()
檢視當頁的 title 是否等於特定字串,若否則顯示客製化訊息(optional)。
例如:檢視當頁的 title 是否為「露天拍賣」。
browser.assert.title('露天拍賣');
.urlContains()
檢視目前網址是否 包含 特定字串,若否則顯示客製化訊息(optional)。
例如:檢視目前網址是否包含「s000.php」。
browser.assert.urlContains('s000.php');
.urlEquals()
檢視目前網址是否 等於 特定字串,若否則顯示客製化訊息(optional)。
例如:檢視目前網址是否等於「s000.php」。
browser.assert.urlEquals('s000.php');
.value()
檢視目前表單元件的值是否 等於 預期的值,若否則顯示客製化訊息(optional)。
例如:檢視 .account
這個 DOM element 的值是否等於「hello_kitty」。
browser.assert.value('.account', 'hello_kitty');
回顧 Expect 的語法。
browser.expect.element('.account').to.have.value.that.equals('hello_kitty');
.valueContains()
檢視目前表單元件的值是否 包含 預期的值,若否則顯示客製化訊息(optional)。
例如:檢視 .account
這個 DOM element 的值是否包含「hello_kitty」。
browser.assert.valueContains('.account', 'hello_kitty');
回顧 Expect 的語法。
browser.expect.element('.account').to.have.value.that.contains('hello_kitty');
範例
檢視露天拍賣的頂層分類頁,會檢視當前網址、標題、特定元素的屬性值、元素是否存在、元素是否可見、表單元件輸入值等。
範例程式碼。
module.exports = {
'Assert Categoty Advertisements': browser => {
browser
// 打開指定網址
.url('http://class.ruten.com.tw/category/main?0008')
// 等待 `<body>` 這個元素出現
.waitForElementVisible('body')
// 檢視網頁標題是否為「相機、攝影機 - 露天拍賣」
.assert.title('相機、攝影機 - 露天拍賣')
// 檢視 .rt-header 這個 DOM element 的屬性 data-module 其值是否包含 rt-header
.assert.attributeEquals('.rt-header', 'data-module', 'rt-header')
// 檢視 .rt-wrap-search 這個 DOM element 的屬性 data-module 其值是否包含 page-class-main
.assert.attributeEquals('.rt-wrap-search', 'data-module', 'page-class-main')
// 檢視 .rt-ad-text-only .rt-ad-item:nth-child(1) 這個 DOM element 是否存在
.assert.elementPresent('.rt-ad-text-only .rt-ad-item:nth-child(1)')
// 檢視 .promo-bar 這個 DOM element 是否存在
.assert.elementPresent('.promo-bar')
// 檢視 .rt-subcategory-list .rt-subcategory-item 這個 DOM element 是否存在
.assert.elementPresent('.rt-subcategory-list .rt-subcategory-item')
// 檢視 #ad-flash 這個 DOM element 是否可見
.assert.visible('#ad-flash')
// 檢視 #ad-flash .rt-ad-item 這個 DOM element 是否存在
.assert.elementPresent('#ad-flash .rt-ad-item')
// 檢視 .rt-flagship 這個 DOM element 是否存在
.assert.elementPresent('.rt-flagship')
// 檢視 .rt-flagship 這個 DOM element 的屬性 data-module 其值是否等於 class-main-flagship
.assert.attributeEquals('.rt-flagship', 'data-module', 'class-main-flagship')
// 檢視 .rt-flagship .rt-ad-item 這個 DOM element 是否存在
.assert.elementPresent('.rt-flagship .rt-ad-item')
// 檢視 .rt-flagship .rt-ad-item:nth-child(1) 這個 DOM element 是否存在
.assert.elementPresent('.rt-flagship .rt-ad-item:nth-child(1)')
// 檢視 #ad-promote .promoted-item:nth-child(4) 這個 DOM element 是否存在
.assert.elementPresent('#ad-promote .promoted-item:nth-child(4)')
// 檢視 #ad-special .special-item:nth-child(3) 這個 DOM element 是否存在
.assert.elementPresent('#ad-special .special-item:nth-child(3)')
// 檢視 .hot-sale-gallery 這個 DOM element 的屬性 data-module 其值是否等於 class-main-slideshow
.assert.attributeEquals('#ad-slideshow', 'data-module', 'class-main-slideshow')
// 檢視 .hot-sale-gallery 這個 DOM element 的屬性 data-module 其值是否等於 class-main-gallery
.assert.attributeEquals('.hot-sale-gallery', 'data-module', 'class-main-gallery')
// 檢視 .hot-sale-item:nth-child(2) 這個 DOM element 是否存在
.assert.elementPresent('.hot-sale-item:nth-child(2)')
// 檢視 #ad-gallery .hot-sale-gallery-item 這個 DOM element 是否存在
.assert.elementPresent('#ad-gallery .hot-sale-gallery-item')
// 檢視 .good-shops 這個 DOM element 是否可見
.assert.visible('.good-shops')
// 檢視 .good-shops .rt-ad-control-item:nth-child(1) .rt-ad-control-link 這個 DOM element 的屬性 href 其值是否包含指定字串,在這裡是放網址
.assert.attributeContains('.good-shops .rt-ad-control-item:nth-child(1) .rt-ad-control-link', 'href', 'https://point.ruten.com.tw/ADI/ap2AD/more.php?CM08')
// 檢視 .good-shops .rt-ad-control-item:nth-child(2) .rt-ad-control-link 這個 DOM element 的屬性 href 其值是否包含指定字串,在這裡是放網址
.assert.attributeContains('.good-shops .rt-ad-control-item:nth-child(2) .rt-ad-control-link', 'href', 'http://pub.ruten.com.tw/adb/ad01_intro.html')
// 檢視 .good-shops .rt-ad-item 這個 DOM element 是否可見
.assert.visible('.good-shops .rt-ad-item')
// 檢視 #ad-featured-list .rt-ad-item 這個 DOM element 是否可見
.assert.visible('#ad-featured-list .rt-ad-item')
// 檢視 .top-sell 這個 DOM element 是否存在
.assert.elementPresent('.top-sell')
// 檢視 .top-sell .rt-ad-item 這個 DOM element 是否存在
.assert.elementPresent('.top-sell .rt-ad-item')
// 檢視 .shopping-mall 這個 DOM element 是否存在
.assert.elementPresent('.shopping-mall')
// 檢視 .shopping-mall .rt-ad-ite 這個 DOM element 是否存在
.assert.elementPresent('.shopping-mall .rt-ad-item')
// 檢視 .shopping-mall .rt-ad-control-item:nth-child(1) .rt-ad-control-link 這個 DOM element 的屬性 href 其值是否包含指定字串,在這裡是放網址
.assert.attributeContains('.shopping-mall .rt-ad-control-item:nth-child(1) .rt-ad-control-link', 'href', 'https://point.ruten.com.tw/ADI/ap2AD/more.php?CS08')
// 檢視 .shopping-mall .rt-ad-control-item:nth-child(2) .rt-ad-control-link 這個 DOM element 的屬性 href 其值是否包含指定字串,在這裡是放網址
.assert.attributeContains('.shopping-mall .rt-ad-control-item:nth-child(2) .rt-ad-control-link', 'href', 'pub.ruten.com.tw/adb/ad02_intro.html')
// 檢視 .rt-ad-search-keyword 這個 DOM element 是否存在
.assert.elementPresent('.rt-ad-search-keyword')
// 檢視 .rt-ad-search-keyword .rt-ad-item 這個 DOM element 是否存在
.assert.elementPresent('.rt-ad-search-keyword .rt-ad-item')
// 檢視 #search_input 這個 DOM element 其值是否為空字串
.assert.value('#search_input', '')
// 對 #search_input 這個 DOM element 鍵入字串「蛋糕」
.setValue('#search_input', '蛋糕', () => {
// 點擊送出按鈕 .rt-site-search-submit
browser.click('.rt-site-search-submit', () => {
// 檢視網址是否包含 s000.php
browser.assert.urlContains('s000.php');
});
})
// 結束 session,關閉瀏覽器
.end();
}
};
這裡為了檢視元素個數而去檢查最後一個元素是否存在或可見,之後會改用客製化斷言指令,針對抓到的元素個數來判斷是否等於預期個數。
// 檢視 #ad-promote .promoted-item:nth-child(4) 這個 DOM element 是否存在
.assert.elementPresent('#ad-promote .promoted-item:nth-child(4)')
執行範例。
nightwatch test/e2e/class/testMainCategoryAssert.js
話說工程師都愛模組化,不要擔心亂糟糟,待之後使用 Page Objects 來改寫 (*´∀`)~♥
Expect vs Assert
基本上 Expect 和 Assert 都可做到相同的事情,對我而言最大的差異就是可否串起來使用。
各位大大如果有任何建議回饋都歡迎提供 (✪ω✪)
備註
Node.js Assert Module
由於 Nightwatch 是使用 Node.js Assert Module,因此不限於此 Nightwatch 文件中提到的指令,只要 Node.js Assert Module 上可用的 method 都可以使用在撰寫測試程式上面喔!
自動重試失敗的斷言
使用 retryAssertionTimeout
設定自動重試失敗斷言的時間,單位是 ms(millisecond)。例如:在 nightwatch.conf.js 中 可以這樣設定…
"test_settings": {
"default": {
"globals": {
"retryAssertionTimeout ": 2000 // 自動重試失敗的斷言的時間
}
}
}
下一篇來看 BDD Verify。