rails rspec測試

基本介紹
      RSpec由Steven Baker開發並在2005年發佈,全面支持Ruby程序的BDD開發測試方式,並且對於Rails程序有着良好的支持,針對View,Controller和Model每一層都有良好的支持。RSpec目前的最新版本是:2.4.0。
詳細介紹
項目週期
      在基於BDD開發模式的項目中,以Rails項目開發爲例,有如下步驟:
1、 對一個項目立項後,首先進行故事分解。要對系統故事分解,要求我們做到基本的需求分析和對系統的概要設計。
2、 專注於一個用戶故事,對其進行詳細定義,這一階段也就是我們編寫RSpec測試用例的階段。通過分析用戶故事和用戶場景的方式對系統的行爲進行詳細的定義,把定義寫在我們的測試用例中。在這一階段,包括了我們的傳統的軟件工程週期中的需求分析和詳細設計階段,需求分析體現在分析系統的行爲上,詳細設計體現在用例編寫時需要爲系統實現定義大量的接口,這一點下邊細講。
3、 對於該用戶故事,實現View層:
      3.1、運行RSpec測試用例,結果失敗;
      3.2、編碼實現該view;
      3.3、重新RSpec運行測試用例,通過;
      3.4、重構代碼;
4、 對於該用戶故事,實現controller層,重複3.1-3.4的過程。
5、 對於該用戶故事,實現model層,重複3.1-3.4的過程。
6、 重構三個層次的代碼,完成該用戶故事。
7、 重複3-6的過程,完成系統其他用戶故事。
8、 系統集成,集成測試,確認測試,beta測試等。
9、 系統交付及後期維護。
      這個過程圖示如下:
     
      這裏強調了按照view、controller到model的順序來做開發,這是BDD鼓勵從外向裏的方式來開發程序,先實現與用戶直接交互的view層,然後是核心業務層controller,最後是與數據庫交互的model層,BDD不但是我們開發程序中運用的一種技術,同時也可以儘早的將系統的用戶接口體現出來,體現系統的商業價值,對於開發,用戶與系統交互時的行爲是系統的核心業務,先將用戶接口開發出來,有助於我們專注於實現核心業務,而不浪費精力於其他方面。
安裝配置
      RSpec的安裝很簡單,利用RubyGems在命令行運行gem install rspec命令就可以爲Ruby安裝rspec的程序包,若是Rails項目,還需要安裝rails-rspec插件(運行gem install rails-rspec),可以通過-v選項指定具體要安裝的版本。需要注意的是Ruby、Ruby on Rails框架、RSpec框架和rails-rspec插件更新速度較快,很容易出現不兼容的問題,總的來說,Ruby版本要1.8.7及其以上才支持Rails3,, Rails3以前的版本不支持RSpec2,RSpec的版本和rails-rspec插件的版本最好一樣,還需注意的是最新版本的Ruby對中文支持較差,必須在Ruby文件的第一行指定編碼方式纔可以正常運行。
      普通的Ruby程序可以直接編寫測試用例並使用”rspec */*.rb”命令(此爲RSpec2的用法,RSpec1用”spec */*.rb”命令運行)來運行spec文件。在一個Rails項目中如何使用RSpec呢?下邊介紹:
1、在GemFile裏面添加如下代碼:
      
      此步作用是爲Rails程序的測試環境和開發環境添加RSpec支持,指定Rails程序需要加載的Rspec程序庫和rspec-rails插件的庫以及他們的版本。
2、在Rails程序根目錄下使用bundler運行”bundle install”命令爲Rails程序安裝依賴的程序包。
3、在Rails程序根目錄下運行”rails generate rspec:install”命令,此命令的作用是爲Rails程序安裝RSpec框架,使RSpec取代Test::Unit而存在,這條命令會建立’.rspec’文件、’spec’目錄和’spec/spec_helper.rb’文件,他們分別的作用是:
   ‘.rspec’文件:存放運行rake spec任務時,RSpec需要加載的配置信息。
   ‘spec’目錄:我們的RSpec測試用例的存放空間,針對mvc,我們可以建立models、views和controllers目錄分別存放模型、視圖和控制器各個層面的測試用例,每一個測試文件以_spec結尾。
   ‘spec/spec_helper.rb’文件:運行測試用例的RSpec配置文件。每一個以_spec結尾的文件都需要引入該文件,即在文件開頭添加:require ‘spec_helper’代碼。
      安裝搞定。
RSpec如何組織測試用例
      先看下邊的代碼:
      
      上邊是一個比較完整的體現RSpec組織測試用例的模板,當然這裏只是爲了說明一個_spec文件如何組織測試用例及基本用法,所以我沒有去編寫每一個測試用例的體,並且例子出現的字符串內容不針對於具體的系統,沒有實際意義。下邊依次解釋含義:
      require ‘spec_helper’:目的是加載’spec/spec_helper.rb’文件中的RSpec配置,運行時需要。
describe()方法:
      我們用describe()方法定義一個測試用例組,describe()方法的參數可以是我們要測試的對象(如例中的People),可以是一個字符串,用來描述該組,describe方法後的字符串所描述的內容可以對應一個用戶故事。
      注意describe()方法是可以嵌套的,兩個describe()方法銜接起來可以更細化一個用戶故事,如上邊的裏面的describe()方法內就表示:”People have enough money pay for house”。
it()方法:
      我們用it()方法定義一個具體的測試用例(在RSpec中,稱一個測試用例爲一個example)。其後的字符串爲該方法的參數,用來描述一個具體的場景,it方法體就是我們對系統在該場景下的行爲的定義。
      It()方法和describe()方法銜接起來,可以完整的描述一個系統行爲,以上邊的最後的一個測試用例爲:”People have enough money pay for house should travel ”。
context()方法:
      Context()方法和describe()方法的作用一樣,不同點在於describe傾向於描述我們的測試對象,而context()方法傾向於用字符串描述用戶故事。
before()和after():
      這兩個方法很多測試框架都支持,需要說明的是這兩個方法的參數,例子中爲符號’:each’,表明對於每一個測試用例,先執行before()方法中的代碼,用例完成後執行after()方法中的代碼,若參數爲’:all’,則表示在所有的測試用例執行之前,先執行before()方法中的代碼,所有的用例完成後執行after()方法中的代碼。
      RSpec還提供around()方法,暫不懂其用法,之後的報告中補上。
幫助方法:
      所謂的幫助方法就是把多個測試用例中重複的操作抽出作爲一個公用的方法,提高代碼重用度。如例子中的work_hard()方法。
共享行爲:
      系統的某一個行爲是很多場景下都具有的,那麼我們可以把它定義爲一個共享行爲,我們通過share_examples_for()方法定義共享行爲,使用it_behaves_like()方法共享定義過的系統行爲,如例子中的share_examples_for “any people”, it_behaves_like “any people”。
pending()方法:
      我們確定系統擁有一個行爲,但是還沒有具體定義,這時我們可以將該行爲使用pending()方法來設置該行爲爲待定義,其後的字符串參數將在生成的報告中顯示。
      pending()方法的另外一種用法就是,當一個測試用例失敗時,我們可以利用pending方法設置其爲一個待修復的bug,方法體內包含使用例失敗的代碼。例如最後一個測試用例的內容。
RSpec如何編寫測試用例
      一個測試用例的永遠都有三個部分:準備請求參數,設置期望輸出,實際輸出和期望輸出做校驗。
      RSpec的測試用例也不另外,不過也存在着很大的區別。區別在於:
      首先於形:代碼接近於描述,一行代碼就是一個句子一樣容易讀懂。
      其次於神:用例在描述系統的一個行爲,他要求我們對系統做了什麼給出定義。
      這兩點是RSpec的核心所在,也是BDD的核心所在。
      對於第一點,得益於Ruby語言本身具有的DSL特性,加之RSpec對其的擴展,使得其實現並不困難。
      對於第二點,是最核心的價值所在,這需要我們傾心設計。
      以前我對於測試用例的看法是,希望一個測試用例儘可能多的覆蓋代碼,這樣容易從集成的角度來尋找bug,BDD認爲一個牽扯過多的測試用例不是一個好的測試用例,它提倡我們儘可能的關注於一點,對於其他的需要執行的代碼通過模擬實現,這一點怎麼做到?答案是:模。
      模是在一個測試用例中實際對象的替代品,我們經常稱之爲模對象。我們可以設置模對象的各種屬性行爲,讓它模擬實際對象的操作,這樣我們的測試用例就可以專注於要測的行爲這一點上,看具體例子:
      
      上邊的這個測試用例描述的行爲是:系統修改bug狀態成功後應該跳到bug首頁並且提示“更新成功“。
      不要被這樣的代碼嚇到,稍作解釋:
      第一行建立一個IssueStatus的模對象;
      第二行的意思是IssueStatus類應該接受find請求(調用IssueStatus的find()方法),請求參數是::id=>11,並且給返回一個名爲issue_status的對象;
      第三行的意思是issue_status對象接受update_attributes請求,請求參數爲::issue_status=>”fixed”時,返回true;
      第四行,使用get方法對系統的update發請求,參數爲::id=>11;
      第五行意思是返回應該重定向到index action上面;
      第六行的意思是提示信息應該是“更新成功“。
      看了下邊的提示以後再回頭看上面的代碼,是不是覺得一行代碼讀下來有讀一個句子的感覺?然後可以按照這種理解的思路讀其他的測試用例中定義的系統的行爲。
      在這裏對測試用例描述系統行爲這一點的理解上還是很混是不是?再解:外圍的describe()方法和it()方法後邊的字符串參數可以幫助我們定位我們要描述的系統的行爲,在測試用例體內描述具體的系統行爲,回到上邊的例子中,我們要描述“修改bug狀態成功“這一行爲,這個行爲時:系統應該根據傳入的id(id是要修改狀態的bug的id)調用IssueStatus類的find()方法得到一個名爲issue_status的對象,通過update_attributes()實例方法來修改issue_status對象的狀態,系統最後應該重定向到index action上面,並且提示“更新成功”的信息。
      再對這個測試用例進行思考,可以發現:驗證的是結果,測試的是過程,描述的是行爲,我們驗證系統有沒有重定向、有沒有提示信息這樣的結果,但是我們也給出了系統應該對相關對象做什麼請求的過程,這些綜合起來構成了系統的一個行爲。這就要求我們對系統接口給出詳細的定義,甚至可以反映系統各模塊之間如何做交互,爲開發人員節省不少時間。
      還可以發現一點,因爲有模代替,系統根本沒有真的去數據庫修改bug狀態,只不過是走了一遍這樣的流程,行爲的概念在這裏無比的清晰。
      下邊代碼是該update()方法的實現:
      
      其中else中的路徑應該另外編寫測試用例來覆蓋。
模的使用
      上邊的例子中採用模代替了部分實現,現在具體介紹一下模的使用方法:
      新建一個模對象:
     
      mock_model()方法專門用於建立model層對象,其餘的三個之間的區別暫時不懂。
      指定模對象的行爲:
     
      第一行stub()方法指定People類接受name()方法調用並且傳入參數爲:id=>0001時,返回“Wu Huanzheng”。with()方法表示傳入的參數,and_return()方法表示返回值。
      第二行should_receive方法具有強制性,表示系統必須要調用指定的方法,其餘和stub()方法一樣。
      第三行at_most(3).times表示至多三次。
      第四行should_not_receive表示明確系統不該調用此方法。
      模對象的行爲很強大,可以模擬一切真實調用,還有很多用法,可以通過查看api獲得。
校驗返回
      RSpec針對所有的返回都有自己的校驗方法,看下邊例子:
     
      第一行result應該等於5;
      第二行result應該包含5;
      第三行result應該響應hello;
      第四行lambda匿名函數應該拋出錯誤信息“Nothing find!”;
      第五行result應該匹配正則表達式;
      第六行期望代碼塊能把bug狀態從“open”改爲“fixed”;
      第七行result應該爲空;
      Should()方法表示肯定,還有should_not()方法表示否定,該方法的工作機制是先執行should或者should_not後邊的代碼,返回一個match對象,進而把此match對象作爲參數傳給should()或者should_not()方法,和擁有對象句柄的本地match對象作對比校驗,若匹配不成功,返回相應信息。瞭解更多使用,也可以通過查閱api獲取。
Rails測試
      瞭解了以上內容,其實Rails每一層的測試也非常明瞭了,具體總結如下:
      View層:讀取每一個控件並渲染,驗證渲染後的結果爲期望值;
      Controller層:使用get或者post方法調用一個action,驗證其滿足用例中描述的行爲;
      Model層:這一層不使用模,需要讀取數據庫裏的數據做驗證。
      RSpec的介紹到此告一段落,理解錯誤之處歡迎指正。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章