使用 Selenium 測試 Ansible 網頁應用程式角色
原文由 Michael Lynch 于 發布,訂閱此部落格
概覽
Ansible 是部署網頁應用程式的絕佳工具。透過 Ansible,我們可以將網頁應用程式定義為組成它的不同「角色(roles)」(例如網頁伺服器、資料庫伺服器、應用程式伺服器)。隨著我們的角色以及它們之間的互動變得愈來愈複雜,我們需要更強大的測試方式來驗證是否正確部署了網頁應用程式。
在上一篇貼文中,我們使用 Ansible 部署了一個名為 ClipBucket 的影片託管網頁應用程式。在那篇文章中,我們加入了自動化測試來驗證網頁應用程式是否正確安裝,但這些測試並沒有很嚴謹地涵蓋應用程式的功能。
在這篇文章中,我們將示範更強大的自動化測試,更深入地測試應用程式的功能。為此,我們會使用一套名為 Selenium 的網頁瀏覽器自動化工具。我們在這裡會繼續使用 ClipBucket 的角色,但這些概念應該可以普遍適用於任何用 Ansible 部署的網頁應用程式。
使用 curl 進行基礎測試
在我們原本的建置腳本中,最後的測試長這樣:
curl -s "${container_ip}" \
| grep "Login" && \
(echo 'Landing page test: pass' && exit 0) || \
(echo 'Landing page test: fail' && exit 1)這是一個有用的測試,因為它驗證了我們網頁應用程式的幾個重要特性,也就是:
- 網頁伺服器正在監聽 port 80
- 網頁伺服器有回應使用者請求
- 網頁伺服器有提供 ClipBucket 的首頁
為什麼我們需要更好的測試
雖然原本的測試提供了一些重要的檢查,但它們幾乎沒有涵蓋到網頁應用程式實際功能的測試。即使我們在 Ansible 角色中引入了會導致網頁應用程式壞掉的錯誤,也有可能無法被這種基礎測試偵測到。
舉例來說,想像我們不小心在 ClipBucket 的 playbook 中刪除了一個關鍵任務:
diff --git a/tasks/main.yml b/tasks/main.yml
index 8542ffc..e9d42c0 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -29,15 +29,6 @@
name: flvtool2
state: present
-- name: create a symlink for ClipBucket to find ffmpeg and MP4Box
- file:
- path: "/usr/local/bin/{{ item }}"
- src: "/usr/bin/{{ item }}"
- state: link
- with_items:
- - ffmpeg
- - MP4Box
-
- name: install the Python MySQLB module
pip: name=MySQL-python如果我們用這個修改過的 playbook 來部署,然後瀏覽目標伺服器,一切看起來都很正常:
所有安裝任務都順利完成,而且我們也看到了 ClipBucket 的首頁。那麼問題在哪呢?
來試著上傳一部影片。一直到我們嘗試觀看影片之前,一切都很正常:
因為我們刪掉了 playbook 中建立 ffmpeg 符號連結的那個任務,ClipBucket 無法將影片轉碼成可串流的格式。
我們想為這個情況建立一個自動化測試,但要用簡單的命令列工具來撰寫上傳影片的腳本相當困難。使用者必須先登入(這代表腳本需要在多次請求之間管理 cookies),然後還要在網頁介面上操作以上傳影片。要用一連串的 curl 指令來做到這些會非常困難。
幸好,我們可以使用Selenium。Selenium 是一套網頁測試工具,讓我們能以程式化的方式執行網頁瀏覽器的操作。
設定 Selenium
要在 Ansible 控制機上開始使用 Selenium,我們需要先安裝幾個元件:
- Selenium Python 套件 - 我們會使用 Python API,這個套件提供了 Selenium 框架與 Python 綁定。
- Firefox - 我們需要一個瀏覽器讓 Selenium 來驅動。雖然 Selenium 支援大多數主流瀏覽器,但它原生就支援 Firefox。
- xvfb - 因為我們會在一台沒有實體顯示器的 VM 上執行這個測試,所以會使用 xvfb 作為虛擬顯示器,讓 Firefox 以為自己是在有螢幕的環境中執行。
我們可以為此建立一個相當簡單的 playbook。唯一比較麻煩的地方是,xvfb 需要一個不太直觀的 init script。幸好,部落客 Cory Klein 去年寫了一篇文章,裡面提供了 xvfb init script 的範例,參考他的範例,我們得以為我們的需求建立一個。
選擇要測試的網頁流程
現在已經安裝好 Selenium,是時候來建立一個 Selenium 腳本來測試我們的網頁應用程式了。我們可能會想驗證的網頁流程有很多種,例如:
- 登入
- 上傳影片並播放
- 在影片下方留言並確認留言有顯示
- 建立新的使用者帳號
對 ClipBucket 來說,我特別想確保影片能正確上傳,所以我們需要測試登入和影片上傳。可惜的是,ClipBucket 使用了一套很重的 JavaScript 套件來處理上傳,因此一般用來上傳檔案的 Selenium API 無法運作。
作為替代方案,我們會改用 ClipBucket 的模組診斷頁面。這個頁面會顯示 ClipBucket 已安裝的模組,當有任何模組沒有正確安裝時,就會顯示錯誤訊息:
我們可以用這個頁面來取代影片上傳流程,以驗證所有模組是否都已正確安裝。
既然已經知道想測試哪些功能,我們就可以大致規劃出想要自動化的網頁流程。它大概會長這樣:
- 載入 ClipBucket 網址
- 以管理員身分登入
- 前往模組診斷頁面
- 驗證所有模組皆已安裝
這會讓我們自動驗證一些在基礎測試中沒有涵蓋到的額外功能:
- 使用者登入功能正常(這代表 ClipBucket 能成功存取資料庫)
- ClipBucket 能存取其相依的工具
自動化網頁流程
要在 Selenium 中自動化瀏覽器操作,我們需要告訴 Selenium 要在瀏覽器中載入哪個 URL,然後讓 Selenium 知道如何在頁面上尋找並與元件互動。接下來我們來看看如何為所需的網頁流程自動化這些操作。
自動化登入
要登入,我們需要在登入頁面上找到帳號密碼欄位,輸入使用者名稱和密碼,然後按下「Login」按鈕。幸好,使用者名稱和密碼欄位都有 id 屬性,讓我們在頁面上很容易就能找到它們。「Login」按鈕雖然沒有 id 屬性,但它有一個值為 login 的 name 屬性,而且在頁面上是唯一的,因此我們可以用它來當作識別依據:
我們在底下這個程式碼片段中定位這些欄位並輸入登入資訊:
self.get('/admin_area/login.php')
username_element = self._driver.find_element_by_id('username')
ui.WebDriverWait(
self._driver, TIMEOUT).until(
expected_conditions.visibility_of(username_element))
password_element = self._driver.find_element_by_id('password')
username_element.send_keys(username)
password_element.send_keys(password)
self._driver.find_element_by_name('login').click()擷取模組清單
檢查已安裝模組頁面的方式有點不同。我們完全不需要與頁面互動,只需要找到並解讀一些頁面上的元件。我們想確認所有模組都已正確安裝,這代表我們需要找出標示每個模組的方塊,然後以程式化的方式判斷它們是否有顯示錯誤訊息。
這有點棘手,因為我們感興趣的元件(或它們在 DOM 中的父層元件)都沒有 id 屬性。它們全都是帶有 class="well" 的 <div>,所以這是我們用來尋找每個模組資訊方塊的最佳方式。
找到這些方塊後,我們需要判斷該方塊顯示的是模組安裝成功還是發生問題。我們可以透過尋找成功的指標,或是確認元件中沒有失敗的指標來判斷。前者比較嚴謹,但後者在程式撰寫上比較簡單。帶有錯誤訊息的方塊一定會包含一個帶有 class="alert" 屬性的元件,所以如果方塊內沒有任何帶有這個 class 的子元件,我們就可以認定它是成功安裝的。
我們可以用底下這個程式碼片段來做到這件事。
self.get('/admin_area/cb_mod_check.php')
for module_element in self._driver.find_elements_by_class_name('well'):
try:
ui.WebDriverWait(
self._driver, TIMEOUT).until(
expected_conditions.visibility_of(module_element))
alert_element = module_element.find_element_by_class_name(
'alert')
if alert_element:
raise ClipBucketModuleError(alert_element.text)
except exceptions.NoSuchElementException:
# Lack of alert is good: the module is installed correctly.
continue在 Travis 中整合網頁流程測試
整合起來後,我們可以把安裝 Selenium 的 playbook 和我們的 Selenium 腳本加入到建置檔中。
接著,我們用先前那個有問題的 commit 來建立一個GitHub pull request。當 Travis 開始建置時,可以看到它失敗了:
2016-09-21 00:17:06,229 clipbucket_driver INFO Logging in with username=testadmin
2016-09-21 00:17:06,229 clipbucket_driver INFO Loading url: http://172.17.0.2/admin_area/login.php
2016-09-21 00:17:07,525 clipbucket_driver INFO Login complete
2016-09-21 00:17:07,526 clipbucket_driver INFO Checking ClipBucket modules
2016-09-21 00:17:07,526 clipbucket_driver INFO Loading url: http://172.17.0.2/admin_area/cb_mod_check.php
Traceback (most recent call last):
File "tests/clipbucket_driver/main.py", line 36, in <module>
main(parser.parse_args())
File "tests/clipbucket_driver/main.py", line 24, in main
driver.do_check_modules()
File "/home/travis/build/mtlynch/ansible-role-clipbucket/tests/clipbucket_driver/clipbucket_driver.py", line 64, in do_check_modules
raise ClipBucketModuleError(alert_element.text)
clipbucket_driver.ClipBucketModuleError: ffmpeg is not found太好了!我們的新測試正確地偵測到了 Ansible 角色中的錯誤。
接著,來確認一下當我們執行正常的 playbook 時,一切是否都能正常運作:
2016-09-21 01:58:21,992 clipbucket_driver INFO Logging in with username=testadmin
2016-09-21 01:58:21,992 clipbucket_driver INFO Loading url: http://172.17.0.2/admin_area/login.php
2016-09-21 01:58:23,036 clipbucket_driver INFO Login complete
2016-09-21 01:58:23,039 clipbucket_driver INFO Checking ClipBucket modules
2016-09-21 01:58:23,040 clipbucket_driver INFO Loading url: http://172.17.0.2/admin_area/cb_mod_check.php
2016-09-21 01:58:23,724 clipbucket_driver INFO Module check complete
2016-09-21 01:58:23,724 clipbucket_driver INFO Exiting ClipBucket driverSelenium 腳本正確判斷出所有必要的模組都已正確安裝。如果未來我們做了任何更動,導致登入功能失效或模組安裝不正確,自動化測試就會幫我們抓出來。
結論
在這篇文章中,我們結合了 Ansible 與 Selenium,來驗證 Ansible 角色是否正確部署了網頁應用程式,以及部署結果是否支援預期的使用者行為。Selenium 提供了高度的彈性,因此可以針對各種網頁應用程式測試許多不同的網頁 UI 流程。
隨機一篇部落格





留言
登入後參與討論