婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 對Ruby on Rails進行高效的單元測試的教程

對Ruby on Rails進行高效的單元測試的教程

熱門標簽:湖州u友防封電銷卡 高德地圖標注客服 滴滴外呼系統 地圖標注賺錢項目注冊 白銀外呼paas系統 徐州網絡外呼系統哪個好 百度地圖標注自定義圖片 電銷機器人廠商代理 常德電銷平臺外呼系統軟件價格


在筆者開發的系統中,有大量的數據需要分析,不僅要求數據分析準確,而且對速度也有一定的要求的。沒有寫測試代碼之前,筆者用幾個很大的方法來實現這種需求。結果可想而知,代碼繁雜,維護困難,難于擴展。借業務調整的機會,筆者痛定思痛,決定從測試代碼做起,并隨著不斷地學習和應用,慢慢體會到測試代碼的好處。

  •     改變思路:能做到從需求到代碼的過程轉換,逐步細化;
  •     簡化代碼:力圖讓每個方法都很小,只專注一件事;
  •     優化代碼:當測試代碼寫不出來,或者需要寫很長的時候,說明代碼是有問題的,是可以被分解的,需要進一步優化;
  •     便于擴展:當擴展新業務或修改舊業務時,如果測試代碼沒有成功,則說明擴展和修改不成功;
  •     時半功倍:貌似寫測試代碼很費時,實際在測試、部署和后續擴展中,測試代碼將節省更多的時間。

環境搭建

筆者采用的測試環境是比較流行通用的框架:RSpec + Factory Girl,并用autotest自動工具。RSpec是一種描述性語言,通過可行的例子描述系統行為,非常容易上手,測試用例非常容易理解。Factory Girl可以很好的幫助構造測試數據,免去了自己寫fixture的煩惱。Autotest能自動運行測試代碼,隨時檢測測試代碼的結果,并且有很多的插件支持,可以讓測試結果顯示的很炫。
第一步 安裝rspec和rspec-rails

在命令行中執行如下命令:

$ sudo gem install rspec v = 1.3.0
$ sudo gem install rspec-rails v = 1.3.2

安裝完成后,進入rails應用所在的目錄,運行如下腳本,生成spec測試框架:

$ script/generate rspec     
  exists lib/tasks
 identical lib/tasks/rspec.rake
 identical script/autospec
 identical script/spec
  exists spec
 identical spec/rcov.opts
 identical spec/spec.opts
 identical spec/spec_helper.rb

第二步 安裝factory-girl


在命令行中執行如下命令:

$ sudo gem install rspec v = 1.3.0
$ sudo gem install rspec-rails v = 1.3.2

安裝完成后,進入rails應用所在的目錄,運行如下腳本,生成spec測試框架:

$ script/generate rspec     
  exists lib/tasks
 identical lib/tasks/rspec.rake
 identical script/autospec
 identical script/spec
  exists spec
 identical spec/rcov.opts
 identical spec/spec.opts
 identical spec/spec_helper.rb

第二步 安裝factory-girl

在命令行中執行如下命令:

$ sudo gem install factory-girl

在config/environment/test.rb中,加入factory-girl這個gem:

config.gem "factory_girl"

在spec/目錄下,增加一個factories.rb的文件,用于所有預先定義的model工廠。
第三步 安裝autotest

在命令行中執行如下命令:

$ sudo gem install ZenTest
$ sudo gem install autotest-rails

然后設置與RSpec的集成,在rails應用的目錄下,運行如下的命令,就可以顯示測試用例的運行結果。

RSPEC=true autotest or autospec

在自己的home目錄下,增加一個.autotest設置所有的Rails應用的autotest插件。當然,也可以把這個文件加到每個應用的根目錄下,這個文件將覆蓋home目錄下的文件設置。autotest的插件很多,筆者用到如下的plugin:

$ sudo gem install autotest-growl
$ sudo gem install autotest-fsevent
$ sudo gem install redgreen

設置.autotest文件,在.autotest中,加入如下代碼。

require 'autotest/growl' 
require 'autotest/fsevent' 
require 'redgreen/autotest' 

Autotest.add_hook :initialize do |autotest|
 %w{.git .svn .hg .DS_Store ._* vendor tmp log doc}.each do |exception|
  autotest.add_exception(exception)
 end
end

測試經驗

安裝了必要的程序庫以后,就可以寫測試代碼了。本例中,所有應用都是在Rails 2.3.4上開發的,RSpec采用的是1.3.0的版本。為了很好的說明問題,我們假定這樣的需求:判斷一個用戶在一個時間段內是否遲到。寫測試代碼時都是遵循一個原則,只關心輸入和輸出,具體的實現并不在測試代碼的考慮范圍之內,是行為驅動開發。根據這個需求,我們將會設計方法absence_at(start_time,end_time),有兩個輸入值start_time和end_time以及一個輸出值,類型是boolean。對應的測試代碼如下:

describe "User absence or not during [start_time,end_time]" do
 before :each do 
  @user = Factory(:user)
 end

 it "should return false when user not absence " do
  start_time = Time.utc(2010,11,9,12,0,0,0)
  end_time = Time.utc(2010,11,9,12,30,0) 
  @user.absence_at(start_time,end_time).should be_false
 end

 it "should return true when user absence " do
  start_time = Time.utc(2010,11,9,13,0,0,0)
  end_time = Time.utc(2010,11,9,13,30,0) 
  @user.absence_at(start_time,end_time).should be_ture
 end
end

測試代碼已經完成。至于absence_at方法我們并不關心它的實現,只要這個方法的結果能讓測試代碼運行結果正確就可以。在此測試代碼的基礎上,就可以大膽地去完成代碼,并根據測試代碼的結果不斷修改代碼直到所有測試用例通過。
Stub的使用

寫測試代碼,最好首先從model開始。因為model的方法能很好與輸入輸出的原則吻合,容易上手。最初的時候,你會發現mock和stub很好用,任何的對象都可以mock,并且在它的基礎上可以stub一些方法,省去構造數據的麻煩,一度讓筆者覺得測試代碼是如此美麗,一步步的深入,才發現自己陷入了stub的誤區。還是引用上面的例子,我們的代碼實現如下:

class User  ActiveRecord::Base
 def absence_at(start_time,end_time)  
  return false if have_connection_or_review?(start_time,end_time)
  return (login_absence_at?(start_time,end_time) ? true : false)  
 end
end

按照最初寫測試代碼的思路,本方法中存在三種情況,即需要三個用例,而且還調用了其他兩個方法,需要對他們進行stub,于是就有了下面的測試代碼。記得當時完成后還很興奮,心中還想:這么寫測試代碼真有趣。

before(:each) do
 @user = User.new
end

describe "method absence_at(start_time,end_time)>" do 
 s = Time.now
 e = s + 30.minutes
 # example one
 it "should be false when user have interaction or review" do
  @user.stub!(:have_connection_or_review?).with(s,e).and_return(true)
  @user.absence_at(s,e).should be_false
 end
  
 # example two
 it "should be true when user has no interaction and he no waiting at platform" do
  @user.stub!(:have_connection_or_review?).with(s,e).and_return(false)
  @user.stub!(:login_absence_at?).with(s,e).and_return(true)
  @user.absence_at(s,e).should be_true
 end

 # example three
 it "should be false when user has no interaction and he waiting at platform" do
  @user.stub!(:have_connection_or_review?).with(s,e).and_return(false)
  @user.stub!(:login_absence_at?).with(s,e).and_return(false)
  @user.absence_at(s,e).should be_false
 end  
end

上面的測試代碼,是典型把代碼的實現細節帶到了測試代碼中,完全是本末倒置的。當然這個測試代碼運行的時候,結果都是正確的。那是因為用stub來假定所有的子方法都是對的,但是如果這個子方法have_connection_or_review?發生變化,它不返回boolean值,那么將會發生什么呢?這個測試代碼依然正確,可怕吧!這都沒有起到測試代碼的作用。

另外,如果是這樣,我們不僅要修改have_connection_or_review?的測試代碼,而且還要修改absence_at的測試代碼。這不是在增大代碼維護量嗎?

相比而言,不用stub的測試代碼,不用修改,如果Factory的數據沒有發生變化,那么測試代碼的結果將是錯誤的,因為have_connection_or_review?沒有通過測試,導致absence_at方法無法正常運行。

其實stub主要是mock一些本方法或者本應用中無法得到的對象,比如在tech_finish?方法中,調用了一個file_service來獲得Record對象的所有文件,在本方法測試代碼運行過程中,無法得到這個service,這時stub就起作用了:

class A  ActiveRecord::Base
 has_many :records
 def tech_finish?
  self.records.each do |v_a|
   return true if v_a.files.size == 5
  end
  return false
 end
end

class Record  ActiveRecord::Base
 belongs_to :a
 has_files # here is a service in gem
end

所對應的測試代碼如下:

describe "tech_finish?" do
 it "should return true when A's records have five files" do
  record = Factory(:record)
  app = Factory(:a,:records=>[record])
  record.stub!(:files).and_return([1,2,3,4,5])   
  app.tech_finish?.should == true
 end

 it "should return false when A's records have less five files" do
  record = Factory(:record)
  app = Factory(:a,:records=>[record])
  record.stub!(:files).and_return([1,2,3,5])   
  app.tech_finish?.should == false
 end
end

Factory的使用

有了這個工廠,可以很方便的構造不同的模擬數據來運行測試代碼。還是上面的例子,如果要測試absence_at方法,涉及到多個model:

  •     HistoryRecord:User的上課記錄
  •     Calendar:User的課程表
  •     Logging:User的日志信息

如果不用factory-girl構造測試數據,我們將不得不在fixture構造這些測試數據。在fixture構造的數據無法指定是那個測試用例使用,但是如果用Factory的話,可以為這個方法專門指定一組測試數據。

Factory.define :user_absence_example,:class => User do |user|
 user.login "test"
 class  user
  def default_history_records
   [Factory.build(:history_record,:started_at=>Time.now),
    Factory.build(:history_record,:started_at=>Time.now)]
  end
  def default_calendars
   [Factory.build(:calendar),
    Factory.build(:calendar)]      
   end
   def default_loggings
   [Factory.build(:logging,:started_at=>1.days.ago),
    Factory.build(:logging,:started_at=>1.days.ago)]
   end
  end
  user.history_records {default_history_records}
  user.calendars {default_calendars}
  user.loggings {default_loggings}
end

這個測試數據的構造工廠,可以放在factories.rb文件中,方便其他測試用例使用,也可以直接放到測試文件的before中,僅供本測試文件使用。通過factory的構造,不僅可以為多個測試用例共享同一組測試數據,而且測試代碼也簡潔明了。

before :each do
 @user = Factory.create(:user_absence_example)
end

Readonly的測試

在筆者的系統中,大量使用了acts_as_readonly,從另外一個數據庫來讀取數據。由于這些model并不在本系統中,所以當用Factory構造測試數據的時候,總會有問題。雖然也可以使用mock來達到這個目的,但是由于mock的局限性,還是無法靈活的滿足構造測試數據的需要。為此,擴展了一些代碼,使得這些model依然可以測試。核心思想則是,根據配置文件的設置,將對應的readonly的表創建在測試數據庫,這個操作在運行測試之前執行,這樣就達到與其他model一樣的效果。site_config配置文件中,關于readonly的配置格式如下:

readonly_for_test:
 logings:
  datetime: created_at
  string: status
  integer: trainer_id

Gem的測試

Gem在Rails中被廣泛使用,而且是最基礎的東西,因此它的準確無誤就顯得更加重要。在不斷實踐的基礎上,筆者所在的團隊總結出一種用spec測試gem的方法。假設我們要測試的gem是platform_base,步驟如下:

1. 在gem的根目錄下創建一個目錄spec(路徑為platform_base/spec)。

2. 在gem的根目錄下創建文件Rakefile(路徑為platform_base/Rakefile),內容如下:

require 'rubygems'
require 'rake'

require 'spec/rake/spectask'

Spec::Rake::SpecTask.new('spec') do |t|
 t.spec_opts = ['--options', "spec/spec.opts"]
 t.spec_files = FileList['spec/**/*_spec.rb']
end

3. 文件在spec目錄下創建spec.opts(路徑為platform_base/spec/spec.opts),內容如下:

復制代碼 代碼如下:
--colour
--format progress
--loadby mtime
--reverse

4. 在spec目錄下,創建一個Rails app,名為test_app。這個新應用需要有spec目錄和spec_helper.rb文件。

5. 為了保持簡化,把這個新app(test_app)整理一下,刪除vendor和public目錄,最終的結構如下:

復制代碼 代碼如下:
test_app
   |- app
   |- config
   |   |- environments
   |   |- initializers
   |   |- app_config.yml
   |   |- boot.rb
   |   |- database.yml
   |   |- environment.rb
   |   \- routes.rb
   |- db
   |   \- test.sqlite3
   |- log
   \- spec
       \- spec_helper.rb

6. 在config/environment.rb配置文件中,增加如下代碼:

Rails::Initializer.run do |config|
 config.gem 'rails_platform_base'
end

7. 在platform_base/spec/目錄下增加helpers_spec.rb文件,內容如下:

require File.join(File.dirname(__FILE__), 'test_app/spec/spec_helper')

describe "helpers" do
 describe "url_of" do
  before do
   Rails.stub!(:env).and_return("development")
   @controller = ActionController::Base.new
  end

  it "should get url from app's configration" do
   @controller.url_of(:article, :comments, :article_id => 1).should == "http://www.idapted.com/article/articles/1/comments"
   @controller.url_of(:article, :comments, :article_id => 1, :params=>{:category=>"good"}).should == "http://www.idapted.com/article/articles/1/comments?category=good"
  end
 end
end

至此,準備工作已經就緒,可以在platform_base目錄下,運行rake spec來進行測試,當然現在什么都不會發生,因為還沒有測試代碼呢。本方法中,最關鍵的就是下面的require語句,不僅加載了Rails environment,而且把gem在test_app中使用并測試。

require File.join(File.dirname(__FILE__), 'test_app/spec/spec_helper')

Controller的測試

對于controller的測試,一般來說比較簡單,基本是三段式:初始化參數、請求方法、返回render或者redirect_to。如下例中,對某個controller的index方法的測試:

describe "index action" do
 it "should render report page with the current month report" do
  controller.stub!(:current_user).and_return(@user)
  get :index,{:flag => “test”}
  response.should render_template("index")
 end
end

有些controller會設置session或者flash,這時的測試代碼就一定要檢查這個值設置的是否正確,而且還需要增加測試用例來覆蓋不同的值,這樣才能對方法進行全面的測試。如下例:

describe "create action" do
 it "should donot create new user with wrong params" do
  post :create
  response.should redirect_to(users_path)
  flash[:notice].should == "Create Fail!"
 end

 it "should create a new user with right params" do
  post :create, {:email => "abc@eleutian.com"}
  response.should redirect_to(users_path)
  flash[:notice].should == "Create Successful!"
 end
end

同時,也需要對controller的assigns進行測試,以保證返回正確的數據。如下例:

before(:each) do
 @course = Factory(:course)
end 

describe "show action" do
 it "should render show page when flag != assess and success" do 
  get :show, :id => @course.id, :flag =>"test"
  response.should render_template("show")
  assigns[:test_paper].should == @course
  assigns[:flag].should == "test"
 end

 it "should render show page when flag == assess and success" do
  get :show, :id => @course.id, :flag =>"assess"
  response.should render_template("show")
  assigns[:test_paper].should == @course
  assigns[:flag].should == "assess"
 end  
end

View的測試

View的測試代碼寫的比較少,基本上是把核心的view部分集成到controller中來測試。主要用integrate_views方法。如下例:

describe AccountsController do
 integrate_views
 describe "index action" do
  it "should render index.rhtml" do
   get :index
   response.should render_template("index")
   response.should have_tag("a[href=?]",new_account_path)
   response.should have_tag("a[href=?]",new_session_path)
  end
 end
end

總結展望

在寫測試代碼的時候,并不一定要事無巨細,有些比較簡單的方法以及Rails的內部的方法,如named_scope,就完全沒有必要測試。本文中,只介紹了用rspec寫單元測試的代碼,對于集成測試沒有涉及,這也是今后努力的一個方向。

另外,用cumumber + rspec + webrat的BDD開發模式也是相當不錯的。尤其是cumumber對需求的描述,完全可以用它來做需求分析。

您可能感興趣的文章:
  • Ruby中嵌套對象轉換成json的方法
  • 在阿里云 (aliyun) 服務器上搭建Ruby On Rails環境
  • ruby安裝gem包失敗的通用解決方法

標簽:普洱 遼寧 張家界 荊門 公主嶺 梧州 三沙 永州

巨人網絡通訊聲明:本文標題《對Ruby on Rails進行高效的單元測試的教程》,本文關鍵詞  對,Ruby,Rails,進行,高效,的,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《對Ruby on Rails進行高效的單元測試的教程》相關的同類信息!
  • 本頁收集關于對Ruby on Rails進行高效的單元測試的教程的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    欧美视频一二三区| 丁香激情综合国产| 中文字幕亚洲电影| 精品国产三级电影在线观看| 欧美日韩一区二区三区不卡| 91久久国产综合久久| 色婷婷久久久久swag精品 | 日韩视频一区二区三区在线播放| 欧洲精品一区二区| 欧美日韩免费电影| 欧美一区二区观看视频| 欧美α欧美αv大片| 日韩午夜电影av| 久久久久国产一区二区三区四区| 国产亚洲欧美激情| ...av二区三区久久精品| 亚洲蜜桃精久久久久久久| 亚洲人成网站色在线观看| 亚洲已满18点击进入久久| 日韩国产欧美在线视频| 久久av资源网| 成人爱爱电影网址| 欧美无砖专区一中文字| 欧美一区二区视频在线观看| 久久久久久久久伊人| 亚洲欧洲精品一区二区三区| 亚洲一区二区在线免费看| 美女被吸乳得到大胸91| 国产成人啪免费观看软件 | 亚洲成人动漫精品| 久久精品噜噜噜成人88aⅴ| 风间由美一区二区三区在线观看| 91麻豆123| 欧美一级夜夜爽| 国产精品第四页| 午夜免费欧美电影| 国产大片一区二区| 欧美一区二区三区视频在线| 国产精品每日更新| 蓝色福利精品导航| 一本大道久久a久久综合婷婷 | 亚洲国产精品成人综合| 亚洲欧美另类小说视频| 精品在线播放免费| 欧美日韩国产综合一区二区| 中文字幕国产一区二区| 日本女人一区二区三区| 色哟哟国产精品免费观看| 久久网这里都是精品| 一区二区三区在线观看国产| 国产一区二区三区黄视频| 欧美日韩视频在线第一区| 国产精品理论在线观看| 麻豆一区二区99久久久久| 色菇凉天天综合网| 国产精品国产三级国产三级人妇| 激情久久五月天| 欧美日韩国产美女| 亚洲午夜精品17c| 一本色道久久综合亚洲aⅴ蜜桃| 久久日一线二线三线suv| 丝瓜av网站精品一区二区| 91日韩精品一区| 中文字幕亚洲视频| 成人97人人超碰人人99| 久久精品在这里| 国产在线精品免费| 精品va天堂亚洲国产| 婷婷国产在线综合| 欧美精品久久一区二区三区| 一区二区三区欧美日韩| 色综合久久天天综合网| 亚洲天堂久久久久久久| 99精品欧美一区二区三区小说| 亚洲国产精品成人综合| 成人精品视频一区二区三区| 久久影视一区二区| 精品亚洲成a人| 久久综合九色欧美综合狠狠| 韩国精品免费视频| 国产欧美日韩综合精品一区二区| 国产精品白丝jk白祙喷水网站| 久久人人爽爽爽人久久久| 丁香五精品蜜臀久久久久99网站| 久久亚洲精精品中文字幕早川悠里 | 日韩欧美精品在线| 久久66热偷产精品| 国产视频911| 99re8在线精品视频免费播放| 国产精品电影一区二区三区| 欧洲精品视频在线观看| 日本视频在线一区| 2019国产精品| 91视频免费看| 轻轻草成人在线| 国产欧美一区二区三区鸳鸯浴 | 国产亚洲综合色| 9人人澡人人爽人人精品| 亚洲黄色免费电影| 日韩免费视频线观看| 成人综合在线视频| 亚洲午夜成aⅴ人片| 久久综合久色欧美综合狠狠| 不卡一区二区在线| 日本欧美大码aⅴ在线播放| 2024国产精品视频| 欧美影片第一页| 国产资源精品在线观看| 亚洲欧美电影一区二区| 精品裸体舞一区二区三区| 成人免费黄色在线| 青娱乐精品在线视频| 国产精品国产三级国产普通话99 | 国产精品区一区二区三| 欧美午夜精品久久久| 国产一区二区三区黄视频 | 国产美女在线精品| 韩国欧美一区二区| 亚洲精品日韩综合观看成人91| 欧美精品v国产精品v日韩精品| 成人精品国产福利| 蓝色福利精品导航| 亚洲第一久久影院| 国产精品久线在线观看| 日韩三级伦理片妻子的秘密按摩| 99精品一区二区| 国产乱国产乱300精品| 午夜影院久久久| 亚洲人亚洲人成电影网站色| 久久久久久久久久久久久女国产乱| 91国偷自产一区二区使用方法| 国产成人av影院| 激情久久久久久久久久久久久久久久| 午夜精品久久久久| 一区二区三区国产| 自拍视频在线观看一区二区| 久久久91精品国产一区二区精品 | 欧美tk—视频vk| 欧美精品丝袜久久久中文字幕| 91蜜桃在线免费视频| 波多野结衣亚洲一区| 国产一区二区三区在线看麻豆| 麻豆国产91在线播放| 日韩精品一区第一页| 亚洲成av人片在线观看无码| 亚洲自拍偷拍网站| 一区二区三区日韩精品| 亚洲美女一区二区三区| 亚洲欧美经典视频| 亚洲午夜免费福利视频| 午夜影视日本亚洲欧洲精品| 偷拍一区二区三区| 日韩成人免费在线| 激情六月婷婷久久| 国产盗摄一区二区| 91蜜桃传媒精品久久久一区二区| www.视频一区| 91久久精品午夜一区二区| 日本黄色一区二区| 欧美另类z0zxhd电影| 7777精品伊人久久久大香线蕉超级流畅 | 精品中文字幕一区二区| 国产综合色在线| 免费在线观看日韩欧美| 久久国产福利国产秒拍| 国产毛片精品视频| 成人av资源下载| 欧美在线观看视频一区二区| 91精品国产麻豆| 国产欧美视频一区二区三区| 亚洲欧洲无码一区二区三区| 性久久久久久久久久久久 | 国产精品全国免费观看高清| 日韩毛片在线免费观看| 亚洲sss视频在线视频| 麻豆精品一区二区三区| 丁香五精品蜜臀久久久久99网站| 一本大道av伊人久久综合| 欧美男男青年gay1069videost| 日韩区在线观看| 亚洲欧洲国产日本综合| 午夜久久久久久久久久一区二区| 精品一区二区三区免费| 97久久超碰国产精品电影| 欧美一区二区三区思思人| 中文字幕在线视频一区| 三级一区在线视频先锋 | 在线免费观看日本一区| 欧美一级日韩一级| 最新国产の精品合集bt伙计| 日本中文字幕一区二区视频| 成人一区二区三区视频| 欧美日韩另类一区| 国产精品卡一卡二| 精品亚洲成a人| 6080亚洲精品一区二区| 亚洲人成影院在线观看| 国产激情偷乱视频一区二区三区| 欧美三级三级三级爽爽爽| 国产精品久久午夜|