淺談 Protractor 的「力」與「美」

Protractor 是一款 end-to-end test framework. 什麼是 end-to-end test 呢?! 簡單的說它可以模擬 user 在 browser 上操作的所有行為(近乎 100%), 進而測試頁面 flow 是否順暢? JavaScript 各 module 是否運作正常? 如果寫得好的話, 取代傳統的人工測試使之進化到全自動化測試也不再是件遙不可及的夢想。

筆者因為工作上的需要, 接觸 protractor 的時間也約莫有一年之久, 從懵懵懂懂一直到現在對於整體脈絡也算是有一定程度上的了解, 期間寫了幾篇相關的分享, 今天想跟大家聊聊有了這些基礎建設後, 後續新增的 testing 是如何有效且迅速的加入既有的的架構中, 充分發揮它的力與美

現在 DevOps 正夯, 在落實之前, 每個環節的 testing 皆重要, 環環皆不可或缺, 很多時候為了 Goal 而 go, 在初期建制完成後, end to end testing 就一直維持在特定的 scope 中, 由於深怕測試不過會影響 release, 所以工程師們更加的卻步, 凡事綁手綁腳, 畏畏縮縮, 一整個虎頭蛇尾樣, 說真的! 這樣的心態是要如何再創下一個黃金十年呢? 機制確立了, 就是要去用, code 自己寫的, 更是責無旁貸, 要提出令人信服的品質與強而有力的數據佐證, 讓 user 隨時隨地都可以有穩健的服務可以使用, 這才是一個「 真 . 工程師 」應有的態度與責任!!

在開始之前, 筆者先假設您已經對先前所分享文章有一定程度上的了解, 如果不熟悉或者有點生疏的朋友們也不用擔心, 可以直接捲動到本文最下方的 延伸閱讀 處慢慢看, 相信對於接下來的內容會有更深入且透徹的理解喔!

今天要跟大家分享的案例是筆者在 Yahoo 奇摩 拍賣上所做的一個功能測試, Yahoo 奇摩 拍賣 日前推出了一個可以讓 user 直接在 商品管理列表上直接編輯商品庫存的功能, 讓賣家在商品的編輯上可以更加迅速的完成

※ 相關公告:  [新功能速報] 商品管理頁新增快速修改數量功能

以下為相關的使用步驟

移動鼠標至庫存資訊位置, 並且點擊編輯 icon 來進入編輯模式

點擊後, user 可以選擇欲修改 L1 和 L2 的 spec

首先我們先選擇欲調整的 L1 spec, 如上所示, 共有五種可以選

再來便可以調整 L2 spec 相對應的庫存量, 完成數量調整後, 直接點擊勾勾的 icon, 來執行確認修改的動作

點擊確認修改後, 便會出現 process 的 icon 來提示 user 系統正在處理中

完成庫存的調整了

有了完整的使用情境後, 自然便可以開始進行 end to end 的 testing 了.

首先我們先來撰寫相關的 scenario, 為了方便日後可以迅速的查找降低 maintain 上的 cost, 我們可以把這個 scenario 放到最適合它的地方.

筆者習慣以頁面屬性來做 scenario 分類, 由於它是屬於商品列表的測試, 因此可以考慮將它放在 merchandiseManagement_itemList.feature 這個相關的 feature 中, 既自然且直覺, 更方便日後維護

※ git:  merchandiseManagement_itemList.feature

Scenario 的撰寫儘量以最小單位下去切割, 這樣子方便我們日後可以賦予它在不同的 testing flow 達到承先啟後的重要使命, 如果該 scenario 包山包海過於龐大的話, 除了不方便 reuse 這個弊端外, 一旦發生錯誤要找到 track 出錯的 step 也會變的困難莫名

@ITEMSTOCKMODIFY @E2E @PP @PROD @C4797511
Scenario: [商品管理][列表][直購品][庫存][編輯][上架中] 庫存可編輯成功
    When I filter item by "buynow" 
    And I filter item by status - "onshelf"    And I pick search type as "mid"     And I search item as "item - buyNow - basic"'s id    Then search result must have more than "1" record    And modify item stock as "33" must correct 

透過自然語言的撰寫, 我們希望不管今天閱讀的對像是誰, 都能夠很清楚的知道這個 scenario 想要表達的意思是什麼!

前面幾句 step 的描述, 主要是希望我們可以在商品列表頁透過 search filter 以及 keyword 的鍵入來找到特定的商品, 最後一句則是描述將庫存修改成 33 件的行為必須要正確. 比較會有爭議的部分也是落在該 step, 這是由於商品屬性的不同, 其修改庫存的 UI 也會有不同的呈現

具備 L1 和 L2 兩層 Spec 的多規格商品

僅有 L1 一層 Spec 的多規格商品, 和雙層規格商品最大的差別就是…少了下拉選單

無規格商品

一般來說可能會因為這三種類型, 便會想說來撰寫三個不同描述的 step 來達到修改庫存功能上的驗證, 不過試著想想, 我們當下開發自然最是清楚什麼類型的商品用什麼用的 step 來做驗證, 但是, 開發完一段時間後, 當需要在 reuse 相關的 step 時, 可能就會手忙腳亂, 不知道該用什麼樣的 step, 甚至還因此再次撰寫新的 step 來達到驗證的目的

這些其實都不是我們所樂見的, 人人都布希望有技術債的包袱, 因此撰寫好 maintain 的 code 更是工程師應近的責任與態度, 也因此, 筆者不會因為商品屬性來區隔不同的 step, 畢竟我們想要的目的只有一個, 那就是….修改庫存這個行為是否正確!!

有了 step 的描述後, 我們會預期的拿到第一個 fail 的紅燈, 因為我們還沒完成該 step 所該做的事情 – stepDefinition, 將 stepDefinition 放在對的位置, 自然也是降低 maintain cost 最直接有效的方法之一, 筆者依照當初 stepDefinition 的檔案命名規則以及屬性來分, 自然就會將之擺放到 sd_merchandiseManagement_itemList.js

※ git: sd_merchandiseManagement_itemList.js

簡單的看一下相關內容

Then(/^modify item stock as "([^"]*)" must correct$/, function(key, next) {
var stand = this.stand, original, request = Number(key); //listMerchandise this.stand.getFirstRowItemStock().then( function(itemStock) { expect(itemStock, 'itemStock missing').not.to.be.undefined; original = itemStock; } ).then( function() { stand.modifyFirstRowStock(request).then( function(flag) { expect(flag, 'itemStock modify fail').to.be.true; } ); } ).then( //recover stand.modifyFirstRowStock(original).then( function(flag) { expect(flag, 'itemStock modify fail').to.be.true; } ) ).then(next, next);});

這個 stepDefinition 作的事情其實很簡單

  1. 先把目前商品的庫存量取出來
  2. 將庫存量修改成我們在 step 上所設定的數字, 在這裡自然就會是 “33”, 在藉由回傳的布林值來之道修改的結果是否正確
  3. 最後, 便是把一開始拿到的庫存量回寫, 避免因為測試上的修改造成其他 scenario 上的錯誤, 適時的恢復犯罪現場是一種美德

那麼這些驗證 method 放在哪裡呢?! 相信大家還記得 pageObject 強大的地方吧! 每一個頁面都會有其相對應的 pageObject, 裡面用存放一些頁面結構上的定義, 以及 user 可能會進行操作的 method, 它就是頁面的實際縮影, 透過 pageObject 的閱讀, 我們更是可以很清楚的知道該頁面具備了哪些操作行為, 日後一但頁面改版, 或者是操作行為有變更的話, 只要 修改/調整 相對應的 pageObject 便可以了, pageObject 的角色可以說是宛若 automation 的靈魂一般

所以囉! 既然目前的頁面是在商品管理頁, 所以我們只要找到商品管理頁的 pageObject, 在把這些頁面結構和方法補上即可

 ※ git: listMerchandise.js ※ git: listMerchandise.getFirstRowItemStock  ※ git: listMerchandise.modifyFirstRowStock 

附帶一提, 三種不同類型的商品結構以及庫存修改行為, 自然就是被封裝以及定義到該 pageObject 中, 另外, 上述的 method 皆具備錯誤處理的能力, 這樣讓使用它的 stepDefinition 可以確確實實拿到所期待的布林值 (即使真的有錯誤發生, 它也不會是因為 exception 中斷)

驗正修改是否正確的行為, 可以透過 reload 該 page 重新取值來做比對, 或者是到其他頁面取值來做比對, 一般來說都會選擇後者, 因為不同頁面拿取 data 的方法可能不盡相同, 透過這樣交互的比對, 更能確定我們所做的測試結果是否正確

有了 scenario, stepDefinition 以及 pageObject 的撰寫好, 便是確切且踏實的完成了我們的測試, 讓現行存在的測試區域得以擴展, 亦是本篇主要想要闡述的重點核心

筆者將它納入主要 testing flow, 並且錄製了相關的測試

 ※ youtube: dragonMultiSpec

主要的測試項目有:

1. 新增一筆多規格商品
2. 商品管理測試下架功能
3. 商品管理測試上架功能
4. 商品管理編輯 title 是否成功
5. 商品管理編輯 price 是否成功
6. 商品管理編輯 庫存是否成功
7. header 存在否
8. footer 存在否
9. 商品 title 是否正確
10. 商品 brief 是否正確
11. 商品定價 是否正確
12. 所有商品圖示正常顯示
13. 商品 description 是否正確
14. 點選商品規格, 左側的預覽模組是否連動
15. 放大鏡是否正常
16. video 是否可以 autoplay
17. 商品發問與回覆是否正常
18. 最愛賣家功能是否正常
19. 追蹤商品功能是否正常
20. 能否加入購物車
21. 能否經由 711 成立訂單
22. 賣家執行出貨
23. 買家給賣家評價
24. 賣家給買家評價
25. 能否經由 FamilyMart 成立訂單
26. 訂單管理測試取消訂單是否成功
27. 能否經由 郵局貨到付款 成立訂單
28. 訂單管理測試取消訂單是否成功
29. 將上述商品進行下架

還記得當初我們在撰寫時是以最小單位來劃分的目的嗎?! 自然就是希望可以達到 reuse 且承先啟後完成不同 testing flow 的強大使命, 所以囉! 搭配之前分享的 dragonKeeper, 我們可以透過簡單的步驟, 完成相關的一條龍測試

第一步, 準備好 egg 的部分

 ※ git: egg - dragonItemStock

第二步, execute dragonKeeper.js

dragonKeeper 的執行結果

第三步, 我們便可以得到 dragonItemStock 這條龍了!

 ※ git : dragonItemStock.feature

只要有心, 隨時隨地都可以敷出各式各樣我們期待的龍喔! ^^

dragonItemStock 的測試項目有:

1. 新增一筆多規格商品
2. 商品管理編輯 庫存是否成功
3. 將上述商品進行下架
4. 新增一筆多規格(Layer x 1)商品
5. 商品管理編輯 庫存是否成功
6. 將上述商品進行下架
7. 新增一筆競標型商品
8. 商品管理編輯 庫存是否成功
9. 將上述商品進行下架

筆者一樣有錄製相關的測試 video, 歡迎有興趣的朋友點閱

 ※ youtube: dragonItemStock

以上, 便是筆者就現行基礎所添加的相關功能測試, 照著本文來做, 便可以有效的讓既有的 automation 得到最大延展以及擴充, 更能讓服務所推出的新功能都能掛上一層保護提供更穩健的服務給 user, 是不是看的您心癢癢的, 躍躍欲試呢?! 與其想這麼多, 不如徹底去實踐它會來的更有義意喔!

如果在閱讀本文或者實作上有任何問題的話, 隨時歡迎一同與筆者討論喔! 成長的路上, 你我相隨並不寂寞, 千萬不要客氣, 讓我們一同努力的發掘 Protractor 的「 力 」 與「 美 」 喔!

※ Reference:

※ 延伸閱讀 :

轉載文章 – 淺談 Protractor 的「力」與「美」

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *