前面大概敘述過 NightwatchJS 的運作方式,接著繼續來說明一些可能會有雷的地方。當然,我覺得這不是每個人都會遇到,應該是我天生比較帶賽的關係。
像是鋤頭打到自己的腳之類的事情。
運作順序
畢竟他還是 JavaScript 的關係,所以運作的方式可能會跟我們想像的有點出入。舉個例子來說,
browser
.verify.ok(true, '第 1 次 Verify')
browser
.click('button[type="submit"], function() {
browser.verify.ok(true, '第 2 次 Verify')
})
.verify.ok(true, '第 3 次 Verify');
基本上,看到回呼 (Callback) 就一定不會是上面那個順序,真實順序是這樣,
- 第 1 次 Verify
- 第 3 次 Verify
- 第 2 次 Verify
所以 Nightwatch 提供了一個 .perform
來確保順序的問題。但是!
BUT!
你以為會這麼順利嗎?
browser
.verify.ok(true, '第 1 次 Verify')
browser
.perform(function(browser, done) {
browser.click('button[type="submit"], function() {
browser.verify.ok(true, '第 2 次 Verify');
done();
})
})
.verify.ok(true, '第 3 次 Verify');
執行結果就是,
- 第 1 次 Verify
- 第 3 次 Verify
- 第 2 次 Verify
因為 .perform
本身是非同步,所以搭配 verify
或是 assert
原生系列 ( *1 ) 的方法,結果還是會一樣。所以 .perform
只對於 Nightwatch 的其他命令會有效果。
所以,你會看到 Nightwatch 官方範例當中,都把 verify
或是 assert
放在最後,或是放在 Callback 裡面去跑。反正測試也不管順序,只管對錯而已,所以在哪裡判斷對錯好像都沒差。
但是不是每一種判斷對錯都沒差的好嗎!
所以,一旦遇到這種狀況,我個人會使用 Promise
並且把一系列的動作,打包成 Page Object 或是 Custom Commands 來執行,可以確保執行結果都結束後,才去做判斷的事情。不過這個有點太進階了,之後或許在找時間來介紹。
*1 主要是 NightwatchJS 的 assert
與 verify
可以使用 NodeJS 的 assert
系列命令,這些在測試步驟中都會偷跑。
.click 的莫名雷
一般來說 .click
代表我用滑鼠去點擊該元件,不過,你有時候會發現好像 沒有效果 。然後使用 Callback 來去追蹤回傳的執行結果,你會發現一些奇妙的狀況,
- 該元件點不到
- 該元件不能點
- 該元件沒有顯示所以點不到
我不知道這是否跟瀏覽器有關,不過主要的起因有幾點,
- 該元件完全沒出現在畫面上,連 DOM Tree 都沒有
- 該元件沒有
click
事件 - 使用套件綁定在非原生擁有
click
事件的元件 - 該元件是出現在 viewport 以外的畫面,外加
body
捲軸被鎖定
所以,扣除第 1 點之外,其他的就得另外想辦法。通常是說,我們可以利用 getLocationInView
來定位元件,真的有定位到之後,再去做 click
的動作。換成我們人工操作的方法就是,
- 先在畫面上看是不是有指定的元件(必須要
visible
- 捲動畫面到可以看到那個元件
- 滑鼠移上去
- 按下滑鼠左鍵
所以說,我在做 .click
之前,會做一個叫 scrollToElement
的命令,
exports.command = function scrollToElement (selector) {
const self = this;
this
.perform(function(browser, done) {
browser
.waitForElementPresent(selector, 5000)
.getLocationInView(selector, function(result) {
browser
.execute('scrollTo(0, ' + result.value.y + ')')
.pause(500)
.moveToElement(selector, 1, 1, function(result) {
done();
});
});
});
return this;
};
那麼我就可以這樣寫,
browser
.scrollToElement('button[type="submit"]')
.click('button[type="submit"]')
可能會有人覺得多此一舉吧(我猜
Callback Hell
在 Nightwatch 當中,每一個命令其實都有 Callback 可以使用。不過,為了確保動作的執行順序,所以會出現一大堆 Callback 的狀況,例如,
browser
.perform(function(browser, done) {
browser.click('button[type="submit"], function() {
browser
.waitForElementVisible('.page-2')
.getText('.pages', function(result) {
if (result.value === 2) {
browser.getAttribute('.page' + result.value, 'class', function(result) {
if (result.value.indexOf('last-page') > -1) {
browser.verify('ok', '最後一頁');
}
});
}
})
})
})
.verify.ok(true, '第 1 次 Verify');
以上是一個很爛的例子。
為了避免這種狀況,就如同我上面說的,可以使用 Promise 或是 async/await 語法糖來包裝。不過,這些情況通常都是相當 機歪特殊的場合才會使用。之後或許在找時間另闢篇幅來說一些這種狀況。
小結
有沒有測試比較難寫的八卦?