10分鐘學會 Cucumber + Watir 自動化測試框架
(一)Cucumber和Watir的關係
Cucumber和Watir可以沒有任何關係,Cucumber只是一個BDD框架,而Watir只是一個Web Driver而已,兩者的共同點是均屬於Ruby世界。
(二)單獨使用Cucumber
由於Cucumber和Watir沒有必然聯絡,因此兩者均可單獨使用,下面就讓我們用Cucumber來寫一個非常簡單的單元測試。 定義一個需要測試的Calculator類如下:
1 class Calculator 2 def add num1, num2 3 num1+num2 4 end 5 end複製程式碼
用於測試Calculator類的add方法的feature檔案如下:
1 Feature: Unit test for Calculator 2 3 Scenario: Add two numbers 4 Given I have a calculator created 5 When I add '3' and '5' 6 Then I should get the result of '8'複製程式碼
對應的step檔案為:
1 require File.join(File.dirname(__FILE__), "../calculator") 2 require 'rspec' 3 4 Given /^I have a calculator created$/ do複製程式碼5 @calculator = Calculator.new 6 end 7 8 When /^I add '([^"]*)' and '([^"]*)'$/ do |num1, num2| 9 @result = @calculator.add(num1.to_i, num2.to_i) 10 end 11 12 Then /^I should get the result of '([^"]*)'$/ do |expected_result| 13 @result.should == expected_result.to_i 14 end
在以上的step檔案中,第1,2行分別require了自定義的Calculator類和rspec(用於assertion,參考第13行的“should”), 第5行新建了一個@calculator例項變數,第9行完成兩個數相加(3+5),第13行為測試斷言。執行cucumber命令,輸出結果如下:
1 Feature: Unit test for Calculator 2 3 Scenario: Add two numbers # features/Calculator.feature:4 4 Given I have a calculator created # features/step_definitions/calculator_step.rb:4 5 When I add '3' and '5' # features/step_definitions/calculator_step.rb:8 6 Then I should get the result of '8' # features/step_definitions/calculator_step.rb:12 7 8 1 scenario (1 passed) 9 3 steps (3 passed) 10 0m0.002s複製程式碼
測試成功,沒有看到任何Watir的影子。
(三)單獨使用Watir
聽說有非常fashionable的office lady用Watir來完成日常例行並且繁瑣的網頁點選工作的(當然不是測試),聽說而已,但是Watir的確可以完成諸如此類的網頁模擬操作,接下類我們就用Watir來完成google搜尋功能,新建watir_google.rb檔案並加入以下內容:
1 require 'watir-webdriver' 2 browser = Watir::Browser.new :chrome3 browser.goto "www.google.com/" 4 browser.text_field(:name => "q").set "ThoughtWorks" 5 browser.button(:name => "btnG").click複製程式碼
當執行到第2行時,一個瀏覽器視窗會自動開啟,之後訪問google主頁(第3行),設定搜尋關鍵詞"ThoughtWorks",最後點選搜尋按鈕,單獨執行Watir成功,沒有任何Cucumber的影子。
(四)用Cucumber+Watir寫自動化測試
由上文可知,Cucumber只是用接近自然語言的方式來描述業務行為,而Watir則只是對人為操作網頁的模擬。當使用Cucumber+Watir實現自動化測試時,通過正則表示式匹配將Cucumber的行為描述與Watir的網頁操作步驟耦合起來即可。同樣以Google搜尋為例,搜尋關鍵字後,我們希望獲得搜尋結果,先用Cucumber完成搜尋行為描述:
1 Feature:Google search 2 Scenario: search for keyword 3 Given I am on google home page 4 When I search for 'ThoughtWorks' 5 Then I should be able to view the search result of 'ThoughtWorks'複製程式碼
對應的Watir程式碼如下:
1 require "rubygems" 2 require "watir-webdriver" 3 require 'rspec'
4 Given /^I am on google home page$/ do 5 @browser = Watir::Browser.new :chrome 6 @browser.goto("www.google.com") 7 end 8 9 When /^I search for '([^"]*)'$/ do |search_text| 10 @browser.text_field(:name => "q").set(search_text) 11 @browser.button(:name => "btnK").click 12 end 13 14 Then /^I should be able to view the search result of '([^"]*)'$/ do |result_text| 15 @browser.text.should include(result_text) 16 end複製程式碼
執行cucumber,一個新的瀏覽器被開啟,顯示結果與(三)中相同。
(五)自動化測試的設計模式:Page物件
在上面的例子中,我們在所有的step中均直接對@browser物件進行操作,對於這樣簡單的例子並無不妥,但是對於動則幾十個甚至上百個頁面的網站來說便顯得過於混亂,既然要面向物件,我們就希望將不同的頁面也用物件來封裝,於是引入Page物件,既可完成對頁面的邏輯封裝,又實現了分層重用。此時位於high-level的Cucumber檔案無需變動,我們只需要定義一個Page物件來封裝Google頁面(google-page.rb):
1 require "rubygems" 2 require "watir-webdriver" 3 require "rspec" 4 5 class GooglePage 6 def initialize 7 @browser = Watir::Browser.new :chrome 8 @browser.goto("www.google.com") 9 end 10 11 def search str 12 @browser.text_field(:name => "q").set(str) 13 @browser.button(:name => "btnK").click 14 end 15 16 def has_text text 17 @browser.text.should include(text) 18 end 19 end複製程式碼
相應的step檔案需要做相應的修改:
1 require File.join(File.dirname(__FILE__), "google-page") 2 3 Given /^I am on google home page$/ do 4 @page = GooglePage.new 5 end 6 7 When /^I search for '([^"]*)'$/ do |search_text| 8 @page.search search_text 9 end 10 11 Then /^I should be able to view the search result of '([^"]*)'$/ do |result_text| 12 @page.has_text result_text 13 end複製程式碼
執行cucumber,一個新的瀏覽器被開啟,顯示結果與(三)中相同。
(六)加入角色使用者
既然是行為驅動,既然是模擬使用者實際操作,那麼直接對Page物件進行操作也顯得不夠了,於是我們引入了角色使用者User物件,對於擁有多種使用者角色的網站來說特別實用。加入User物件之後,step檔案中不再出現對Page物件的直接引用,而是在User物件的行為方法中進行引用,定義User物件如下(user.rb):
1 require File.join(File.dirname(__FILE__), "google-page") 2 3 class User 4 def initialize 5 @browser = Watir::Browser.new :chrome 6 end 7 8 def visit_google 9 @page = GooglePage.new(@browser) 10 end 11 12 def search_text text 13 @page.search text 14 end 15 16 def assert_text_exist text 17 @page.has_text text 18 end複製程式碼
feature檔案保持不變,在step檔案用User代替Page:
1 require File.join(File.dirname(__FILE__), "user") 2 3 Given /^I am on google home page$/ do 4 @user = User.new 5 @user.visit_google 6 end 7 8 When /^I search for '([^"]*)'$/ do |search_text| 9 @user.search_text search_text 10 end 11 12 Then /^I should be able to view the search result of '([^"]*)'$/ do |result_text| 13 @user.assert_text_exist result_text 14 end複製程式碼
執行cucumber,一個新的瀏覽器被開啟,顯示結果與(三)中相同。
對於擁有多個使用者角色的網站,比如又customer,administrator等,可分別對這些角色定義相應的物件,再在step檔案中應用這些角色物件即可。
(七)用ruby的Module來封裝不同的行為功能
對於單個使用者來說,比如網上購物網站的customer,既要購物操作,又要能修改自己的profile,此時為了對這些不同的邏輯功能進行組織,可引入ruby中的Module來進行封裝,即將costomer的不同行為功能模組封裝在不同的module中,然後在customer物件中include這些Module。為簡單起見,依然用Google搜尋來進行演示,此時可將搜尋功能加入到Module中,定義搜尋module(search-behavior.rb)如下:
1 module SearchBehavior 2 3 def visit_google 4 @page = GooglePage.new(@browser) 5 end 6 7 def search_text text 8 @page.search text 9 end 10 11 def assert_text_exist text 12 @page.has_text text 13 end 14 15 end複製程式碼
在User物件中include該Module:
1 require File.join(File.dirname(__FILE__), "search-behavior") 2 class User 3 include SearchBehavior 4 def initialize 5 @browser = Watir::Browser.new :chrome6 end複製程式碼
對step檔案和feature檔案均不用修改,執行cucumber,一個新的瀏覽器被開啟,顯示結果與(三)中相同。
(八)總結
我們可以在Cucumber對應的step檔案中直接訪問Watir的API,這樣的確也能達到測試目的,但這樣的缺點在於缺少設計,於是我們引入Page物件來封裝不同的頁面,引入使用者角色管理不同的使用者行為,再引入Module來組織不同的功能模組,最後重構成了一個簡單實用的自動化測試框架。