Web 應用程序的驗收測試常常涉及一些手工任務,例如打開一個瀏覽器,并執行一個測試用例中所描述的操作。但是手工執行的任務容易出現操作人員人為的錯誤,也比較費時間。因此,盡可能將這些任務自動化,以消除人為因素,這是一種很好的做法。于是 Selenium 之類的測試工具就有了用武之地。Selenium 幫助您自動化驗收測試,從而可以構建經過更嚴格測試、因而更為可靠也更易于維護的軟件。
驗收測試也稱黑盒測試和功能測試,是測試和檢驗應用程序是否能按照涉眾(stakeholder)的功能性需求、非功能性需求和其他重要需求來運行的一種方法。驗收測試是單元測試和組合測試的補充,后兩者通常是使用 xUnit 框架編寫的。驗收測試也可以使用編程語言來編寫,但是 Selenium 和其他類似的工具,例如 Fitnesse,也支持用特定于工具的文檔格式編寫測試。
驗收測試與單元測試和組合測試有以下不同之處:
* 應用程序是作為一個完整的端到端實體來測試的,而不是像單元測試和組合測試那樣,只是測試一個類或一組類。背景知識
在討論 Selenium 之前,我要介紹關于以下三個話題的一些背景知識,因為這些話題雖然不是本文的主題,但是和本文密切相關:
* 持續集成持續集成
持續集成的目標是自動化構建和測試過程,以便每天自動運行一次或多次這些過程,而不是每個月手動地運行一次。使用持續集成的最大好處是,代碼的更改會定期地自動被集成。如果系統受損,沒有構建成功,Apache Continuum 和 Luntbuild 之類的持續集成工具可以自動通過發送電子郵件通知團隊(見 參考資料)。
Ajax
Ajax 是 Asynchronous JavaScript and XML 的縮寫,這是為相當老的技術新創造的一個術語。Ajax 背后的主要思想是,由于只需更新部分頁面而不是整個頁面,所以 Web 應用程序可以更快地對用戶操作做出響應。
Ajax 將更多的復雜性引入到 Web 應用程序中,這一點也反映在測試中。這是因為 Ajax 就像它的名稱所表明的那樣,使用 JavaScript 和異步 HTTP 請求來更新頁面內容。每個瀏覽器在實現中與其他瀏覽器相比有一些小小的不同。Selenium 是測試和檢測這些差異的很好的工具,因為它在大多數流行的瀏覽器中都能夠運行。
Ruby/Ruby on Rails
Ruby 是一種開放源碼的解釋型腳本語言,用于快捷、容易地進行面向對象程序設計。它提供了大量的庫,而且簡單易用,還具有可擴展性和可移植性。該語言是由 Yukihiro “Matz” Matsumoto 創造的,目的是讓程序員將更多的注意力放在手頭的任務上,擺脫語法的煩惱。
Rails 是由 David Heinemeier Hansson 創造的一種全棧的(full-stack)、開放源碼的 Ruby Web 框架。Rails 的目標是使現實中的應用程序編寫起來需要的代碼更少,并且比 J2EE 和 XML 之類的語言更容易。所有層都能夠無縫地一起工作,因此可以使用一種語言編寫從模板到控制流乃至業務邏輯的各種東西。Rails 使用 YAML 而不是 XML 配置文件以及注釋形式的反射和運行時擴展。這里不存在編譯階段 —— 程序修改后將直接運行。
什么是 Selenium?
Selenium 是 ThoughtWorks 專門為 Web 應用程序編寫的一個驗收測試工具。據 Selenium 主頁所說,與其他測試工具相比,使用 Selenium 的最大好處是:
Selenium 測試直接在瀏覽器中運行,就像真實用戶所做的一樣。Selenium 測試可以在 Windows、Linux 和 MacintoshAnd 上的 Internet Explorer、Mozilla 和 Firefox 中運行。其他測試工具都不能覆蓋如此多的平臺。
使用 Selenium 和在瀏覽器中運行測試還有很多其他好處。下面是主要的兩大好處:
* 通過編寫模仿用戶操作的 Selenium 測試腳本,可以從終端用戶的角度來測試應用程序。Selenium 的核心,也稱 browser bot,是用 JavaScript 編寫的。這使得測試腳本可以在受支持的瀏覽器中運行。browser bot 負責執行從測試腳本接收到的命令,測試腳本要么是用 HTML 的表布局編寫的,要么是使用一種受支持的編程語言編寫的。
Selenium 適用于以下瀏覽器:
Internet Explorer | Mozilla | Firefox | Safari | |
Windows XP |
6.0 | 1.6+, 1.7+ | 0.8+, 0.9+, 1.0 | |
Red Hat Linux |
1.6+, 1.7+ | 0.8+, 0.9+, 1.0+ | ||
Mac OS X 10.3 |
不支持 | 1.6+, 1.7+ | 0.8+, 0.9+, 1.0+ | 1.3+ |
Selenium 命令
通過 Selenium 命令,腳本編寫者可以描述 browser bot 在瀏覽器中所執行的操作??梢詫⑦@些命令分成兩類 —— 操作(action) 和斷言(assertion):
* 操作模擬用戶與 Web 應用程序的交互。例如,單擊一個按鈕和填寫一個表單,這些都是常見的用戶操作,可以用 Selenium 命令來自動化這些操作。在 Selenium 網站上可以找到可用命令的完整列表(見 參考資料)。
Selenium 模式
可以按兩種模式來使用 Selenium:test runner 和 driven。這兩種模式在復雜性和編寫方式方面有所不同。driven 測試腳本編寫起來往往要更復雜一些,因為它們是用編程語言編寫的。但是如果使用 Python 或 Ruby 之類的高級動態編程語言,那么這種復雜性方面的差異就很小。
兩種模式之間最大的不同點在于,如果使用 driven 腳本,測試有一部分在瀏覽器之外運行,而如果使用 test runner 腳本的話,測試是完全在瀏覽器中運行的。
不管是 test runner 還是 driven 測試用例,都可以與持續集成工具集成。
test runner 模式
Selenium test runner 腳本,也稱測試用例(test case),是用 HTML 語言通過一個簡單的表布局編寫的,如 清單 1所示。
清單 1. Selenium 測試用例的結構
clearcase/" target="_blank" >cccccc><table border="1"> <tr> <td>First command</td> <td>Target</td> <td>Value</td> </tr> <tr> <td>Second command</td> <td>Target</td> <td>Value</td> </tr> </table> |
test runner 腳本通常與所測試的應用程序(AUT)部署在同一個服務器上。這是因為 browser bot 使用 JavaScript 來模擬用戶操作。這些腳本在一個受限制的沙箱環境中運行。如果需要繞過這些限制,可以使用一個代理。
test runner 腳本使用與 xUnit 框架相同的測試套件(test suite)和測試用例概念。測試用例和命令按照它們在測試套件和測試用例中出現的順序依次執行。在 清單 1 中:
* 第一列包含命令 或斷言。即使對于非技術人員來說,test runner 腳本也易于閱讀和編寫。當在一個瀏覽器中打開 清單 1 中的例子時,將得到類似這樣的一個表:
First command | Target | Value |
Second command | Target | Value |
接下來,我將描述如何使用命令和斷言編寫一個簡單但是完整的測試用例。
測試用例實例
執行 清單 2 中的測試腳本時,它將執行以下操作:
1. 通過進入 /change_address_form.html 打開變更地址頁面。清單 2. 在測試用例中使用命令和斷言的例子
<table> <tr> <td>open</td> <td>/change_address_form.html</td> <td></td> </tr> <tr> <td>type</td> <td>address_field</td> <td>Betelgeuse state prison</td> </tr> <tr> <td>clickAndWait</td> <td>//input[@name='Submit']</td> <td></td> </tr> <tr> <td>verifyTextPresent</td> <td>Address change successful</td> <td></td> </tr> </table> |
測試套件
要達到對應用程序的完全測試覆蓋,通常需要不止一個測試用例。這就是 Selenium 使用測試套件的原因。測試套件用于將具有類似功能的一些測試用例編成一組,以便讓它們按順序運行。
測試套件和測試用例一樣,都是用簡單的 HTML 表編寫的。Selenium 執行的缺省測試套件的名稱是 TestSuite.html。清單 3 展示了一個測試套件,該套件像通常的用戶一樣測試應用程序。注意,測試套件使用一個只包含一列的表,表中的每一行指向一個包含某個測試用例的文件。
清單 3. 測試套件示例
<table> <tr> <td>Test suite for the whole application</td> </tr> <tr> <td><a href="test_main_page.html">Access main page</a></td> </tr> <tr> <td><a href="test_login.html">Login to application</a></td> </tr> <tr> <td><a href="test_address_change.html">Change address</a></td> </tr> <tr> <td><a href="test_logout.html">Logout from application</a></td> </tr> </table> |
接下來我將把目光轉移到 driven 測試腳本。
driven 模式
driven Selenium 腳本是用多種受支持的編程語言中的一種編寫的 —— 目前可用的有 Java、Ruby 和 Python 驅動程序。這些腳本在瀏覽器之外的一個單獨的進程中運行。驅動程序的任務是執行測試腳本,并通過與運行在瀏覽器中的 browser bot 進行通信來驅動瀏覽器。驅動程序與 browser bot 之間的通信使用一種簡單的特定于 Selenium 的連接語言 Selenese。
driven 腳本比 test runner 腳本更強大、更靈活,可以將它們與 xUnit 框架集成。driven 腳本的缺點(與 test runner 腳本相比)是,這種腳本編寫和部署起來更復雜。這是因為驅動程序必須執行以下任務:
* 啟動服務器。driven 腳本更依賴于應用程序運行時環境。例如,Java 驅動程序使用一個嵌入式 Jetty 或 Tomcat 實例來部署所測試的應用程序。目前,已經有人在致力于將 Selenium 集成到 Ruby on Rails 中,但是在我撰寫本文之際,這個集成版本還沒有被發布。
清單 4 摘自一個使用 Ruby 驅動程序的 driven 測試腳本。注意,我省略了用于啟動服務器和瀏覽器的步驟,這個測試腳本代碼幾乎和 test runner 腳本一樣簡單。
清單 4. 使用 Ruby 驅動程序的例子
puts selenium.open('/logout.html') puts selenium.verify_location('/index.html') |
現實中的需求
在接下來的兩節(現實中的需求 和 現實中的用例)中,我將描述如何在現實場景中使用 Selenium,并針對用 Ruby on Rails 和一點兒 Ajax 技術編寫的一個簡單的股票報價查看器應用程序編寫 Selenium 測試用例。雖然這個應用程序是用 Ruby on Rails 編寫的,但是也可以將這個例子應用于任何 Web 應用程序,因為測試腳本是按 test runner 模式以 HTML 編寫的。這個示例應用程序是用 Ruby 1.8.3 和 Ruby on Rails 0.14.2 測試的,但是它也可能可以使用更舊的或更新的版本。
如果有 Linux,那么發行版中通常已經包括了 Ruby。在命令提示符下運行 ruby -v,檢查您所擁有的版本。對于大多數平臺,都可以在 http://www.ruby-lang.org/ 上找到一個 Ruby 發行版。
接下來的步驟是通過 RubyGems 打包系統安裝 Ruby on Rails。為此,只需執行 gem install rails --include-dependencies。在某些平臺上,必須執行一些額外的步驟,所以請訪問 Ruby on Rails 網站,以獲得更多細節。
在我撰寫本文之際,目前可用的 Selenium 版本是 0.6。我已經將它集成在示例應用程序中(見 下載 小節),我的做法是從 http://selenium.thoughtworks.com/ 下載 Selenium Core 包,然后將名為 selenium 的文件夾復制到用于靜態內容的文件夾。在 Ruby on Rails 應用程序中,這個文件夾的名稱是 public。在 J2EE Web 應用程序中,可以將 selenium 文件夾放在 Web 應用程序的根目錄或 WAR 歸檔文件中。
最后一步是下載示例應用程序。從 下載 小節中獲得這個包。解壓應用程序,并打開一個命令提示符。然后轉入應用程序被解壓到的那個目錄。為了啟動應用程序,運行 ruby script/server。應該看到 Rails 成功啟動了,如 圖 1 所示。
圖 1. 從命令提示符下運行 Ruby on Rails
現實中的用例
在本節中,我將列出示例應用程序的用例。通過這些簡化的用例,可以編寫模擬用戶所執行步驟的驗收測試,并驗證這些步驟的結果是否與預期相符。股票報價應用程序實現了以下四個用例:
* 登錄實現這些用例的代碼已經編寫好了;可以在 app 目錄中找到該代碼,測試用例在 public/selenium/tests 文件夾中。
登錄用例
大多數人都知道登錄頁面是如何工作的 —— 輸入用戶名和密碼,然后將數據提交到服務器。如果憑證有效,就可以成功登錄,并看到受安全保護的資源。在示例應用程序中,這個測試用例包含以下用戶操作和斷言,必須將它轉換成一個 Selenium 測試用例:
1. 單擊登錄鏈接。圖 2 展示了用于這些需求的 Selenium 測試用例。注意,我是在運行測試之后截取屏幕快照的。綠色箭頭表示成功地通過驗證的斷言。
圖 2. 登錄和查看股票測試用例
查看股票測試用例
查看股票頁面顯示一個公司列表。用于這個頁面的測試用例非常簡單,所以被包括在前一個測試用例的后面。該測試用例驗證當前位置是否為 /main/list_stocks,以及頁面是否包含文本 Click on a company name to view details。
查看股票細節用例
查看股票細節用例是在查看股票頁面上觸發的。用戶在一個公司名稱上單擊鼠標時,就觸發了到服務器的一個 Ajax 請求。服務器的響應包括該公司的詳細信息,這些信息將插入到當前頁面中,而不必重新裝載完整的頁面。用于這個用例的測試用例執行以下用戶操作:
1. 單擊公司名稱 Acme Oil。由于使用了 Ajax,請求是異步發生的。在一般的 Web 應用程序中,所有東西通常都是同步的,因此這一點提出了一種不同的挑戰??梢韵駵y試其他功能一樣來測試 Ajax 功能。惟一的不同是,必須讓 Selenium 暫停,等待 Ajax 命令完成。為此,可以使用 pause 命令來等待 Ajax 命令的完成。另外,Joseph Moore 在他最近的 blog 貼中提到,還可以使用 waitForValue 和 waitForCondition 命令代替 pause 命令(見 參考資料)。
圖 3 展示了被轉換成 Selenium 用例的需求。
圖 3. 查看股票細節測試用例
注意 pause 命令:必須使用這些命令,以便等待異步請求完成和更新頁面內容。如果沒有 500 毫秒的暫停,測試將失?。ㄈ?圖 4 所示)。
圖 4. 失敗的查看股票細節測試用例
pause 命令還測試 Ajax 功能的非功能性需求。500 毫秒對于 pause 命令是一個很好的值,因為 Ajax 請求應該快速地執行和完成??梢栽囍サ?pause 命令,看看結果如何。如果測試在您的機器上失敗,那么試著將這個值增加到 1000 毫秒。
退出用例
退出用例很容易實現,簡單來說只有以下兩步:
1. 單擊退出鏈接。圖 5 展示了最后這個測試用例。
圖 5. 退出用例
所有測試都被添加到 圖 6 左側顯示的缺省測試套件中。
圖 6. 示例應用程序的測試套件
執行測試套件
最后要做的是在 Mozilla Firefox 和 Microsoft Internet Explorer 中執行測試套件。為此,在瀏覽器中打開 http://localhost:3000/selenium/TestRunner.html,然后單擊 圖 6 中所示的 All 按鈕。失敗的測試用例和斷言將被標記為紅色,但是這里,在兩個瀏覽器中所有用例都應該可以成功完成(同樣見 圖 6)。注意,我使用的是 Mozilla Firefox 1.0.7 和 Internet Explorer 6.0。
還可以單步調試測試套件,這意味著 Selenium 將很慢地執行測試套件,這樣當測試套件在瀏覽器中執行時,就可以看到它的每一步。
結束語
Selenium 是軟件工程師、設計人員和測試人員的工具箱中又一個有用且重要的工具。通過將該工具與持續集成工具相結合,團隊就可以將驗收測試自動化,并構建更好的軟件,因為他們可以更容易、更早、更頻繁地發現 bug。Selenium 的另一個優點是可以節省時間,使開發人員和測試人員不必將時間花在本可以(也應該)自動化的手工任務上,從而讓團隊將精力放在更有價值的活動上。
原文轉自:http://www.anti-gravitydesign.com