淺談(一):在rails中如何用 rspec 寫模型的測試

話說昨日,健哥讓我分享下怎么用rspec寫模型的測試,頓時一臉懵逼访锻,因為只會些拳腳貓功夫垢揩,趕緊百度谷歌相關(guān)知識,七湊八湊亏推,湊出來下面這篇總結(jié)学赛,參考過的文檔和博客如下(感興趣的可以去看看):

  1. rspec入門教程: http://www.reibang.com/p/1db9ee327357
  2. 1000 個小時學(xué)會 Rails - 003 RSpec 行為驅(qū)動測試簡介: https://ruby-china.org/topics/2848
  3. RSpec 中 let 和 subject 的區(qū)別:https://ruby-china.org/topics/9271
  4. Ruby on Rails 自動化測試: https://ihower.tw/rails4/testing.html
  5. let和before的區(qū)別: http://stackoverflow.com/questions/5974360/rspec-difference-between-let-and-before-block

Step1. 新建一個rails應(yīng)用

話不多說,讓我們創(chuàng)建一個rails 新應(yīng)用吞杭,從頭開始體驗這一奇妙的旅程盏浇;

rails new demo

該應(yīng)用結(jié)構(gòu)如下:

Paste_Image.png

可以看到,在rails中芽狗,默認(rèn)使用的測試工具是Test::Unit绢掰,而不是Rspec,怎么替換掉呢?

Step 2: 安裝 Rspec

在Rails的配置文件Gemfile配置文件中,配置下面信息

group :development, :test do 
    gem 'byebug', platform: :mri  
    gem 'rspec-rails', '3.5.1' 
    # gem 'factory_girl_rails', '4.7.0'  
    # gem 'faker', '1.6.6'
end

我們沒有必要單獨的安裝RSpec, 因為它是rspec-rails的依賴件會被自動安裝童擎, 執(zhí)行bundle install 或者bundle install --without production來安裝使用的gem.
執(zhí)行安裝RSpec的命令:

rails generate rspec:install

該命令執(zhí)行完畢之后,會產(chǎn)生一個文件夾spec,該文件夾下面有spec/spec_helper.rb這個文件,spec_helper.rb用來設(shè)置測試的配置信息.

Paste_Image.png

下面是spec的固定的規(guī)范,固定的格式.

describe XXX do 
  it XXX do ...... 
  end
end

包含了一個 describe 塊以及其中的一個測試用例(sample)滴劲,以 it "..." do 開頭的代碼塊就是一個用例.
可以看到,spec文件夾以及相關(guān)文件創(chuàng)建完成。


Paste_Image.png

Step 3:創(chuàng)建模型和方法

既然我們要介紹模型測試的方法顾复,那么讓我們創(chuàng)建一個模型: Girl(女孩班挖,相信大家都比較喜歡),來做一個簡單的測試演示:

rails generate model Girl 

該命令創(chuàng)建的文件如下:


Paste_Image.png

note:在spec/models/ 創(chuàng)建的 girl_spec.rb文件就是我們寫測試用例的地方(一般這種文件的后綴常用_spec)芯砸。
創(chuàng)建模型后, 執(zhí)行遷移命令:

bin/rake db:migrate

下面讓我們開始編寫模型方法:
既然是girl萧芙,大家肯定都關(guān)心其是否單身给梅,是否漂亮。
如果單身的話双揪,就代表自己有機會追到她破喻,如果既單身又漂亮的話,一般就會符合我等屌絲的口味盟榴。
我們定義這兩個屬性曹质,以及對應(yīng)的方法;
app/models/girl.rb 文件內(nèi)容如下:

class Girl < ApplicationRecord  
    attr_accessor :single, :beautiful
    def initialize(params)    
        self.single = params[:single]    
        self.beautiful = params[:beautiful]  
    end  
    # 是否有機會  
    def have_chance?    
        self.single  
    end  
    # 是否符合你的口味  
    def suit_my_taste??    
        self.single && self.beautiful  
    end
end

Step 4: 編寫測試用例 并執(zhí)行

針對我們編寫的兩個模型方法擎场,我們開始在girl_spec.rb文件中編寫相應(yīng)的測試:
以 it "..." do 開頭的代碼塊就是一個用例.
首先構(gòu)建一個單身羽德,但不漂亮的女孩,來作為我們的測試數(shù)據(jù)迅办;
expect 語句 可以對返回的結(jié)果進行期望值處理宅静,滿足expect語句,即代表通過此測試用例站欺;

require 'rails_helper'
RSpec.describe Girl, type: :model do
  # 測試用例1 
  # 創(chuàng)建了一個 單身 但不漂亮的 女孩, 來驗證 have_chance? 方法
  it "have chance? {single but not beautiful}" do
    params = { single: true, beautiful: false }
    girl = Girl.new(params)
    expect(girl.have_chance?).to eq(true)
  end
end

rspec測試命令格式: ruby rspec file_path/file_name
運行如下測試命令:運行g(shù)irl_spec.rb文件中的所有測試用例

rspec spec/models/girl_spec.rb

運行結(jié)果如下圖:

Paste_Image.png

Test::Unit一樣姨夹,用一個極富深意的點,告訴我們測試通過矾策!太棒了磷账!這是我們寫的第一個 RSpec測試,歡呼贾虽!
1 example, 0 failures 代表: 總共1個測試用例逃糟,有0個運行失敗蓬豁;
接下來绰咽,書寫第2個測試用例-(測試該女孩是否符合你的口味)
girl_spec.rb文件中,添加第2個測試用例:

require 'rails_helper'
RSpec.describe Girl, type: :model do

  # 測試用例1
  # 創(chuàng)建了一個 單身 但不漂亮的 女孩, 來驗證 have_chance? 方法
  it "have chance? {single but not beautiful}" do
    params = { single: true, beautiful: false }
    girl = Girl.new(params)
    expect(girl.have_chance?).to eq(true)
  end

  # 測試用例2
  # 創(chuàng)建了一個 單身 但不漂亮的 女孩, 來驗證 suit_my_taste? 方法
  it "suit my taste? {single but not beautiful}" do
    params = { single: true, beautiful: false }
    girl = Girl.new(params)
    expect(girl.suit_my_taste?).to eq(true)
  end
end

運行如下測試命令:運行g(shù)irl_spec.rb文件中的所有測試用例

rspec spec/models/girl_spec.rb

運行結(jié)果如下圖:

picture

如圖所示:首行返回了 .F, "."代表第1個測試用例通過地粪,"F"第2個測試用例沒通過(False)
Failures(失敗詳情中 )可以看到:
expected:true, got:false,(期望值true, 實際獲得false), 所以沒通過取募。
2 examples, 1 failure 代表: 總共2個測試用例,有1個運行失旙〖肌玩敏;
解釋:很顯然,在我們expect語句中付魔,期望值是true聊品,實際上,該女孩單身几苍,但不漂亮;suit_my_taste?方法 會返回false; 所以無法通過expect語句陈哑,則該測試用例會返回false妻坝,代表沒通過此測試用例伸眶;

接下來,在第3個測試用例中刽宪,創(chuàng)建一個 既單身又漂亮的女孩 來測試 suit_my_taste? 方法 的返回結(jié)果厘贼;

require 'rails_helper'
RSpec.describe Girl, type: :model do

  # 測試用例1
  # 創(chuàng)建了一個 單身 但不漂亮的 女孩, 來驗證 have_chance? 方法
  it "have chance? {single but not beautiful}" do
    params = { single: true, beautiful: false }
    girl = Girl.new(params)
    expect(girl.have_chance?).to eq(true)
  end

  # 測試用例2
  # 創(chuàng)建了一個 單身 但不漂亮的 女孩, 來驗證 suit_my_taste? 方法
  it "suit my taste? {single but not beautiful}" do
    params = { single: true, beautiful: false }
    girl = Girl.new(params)
    expect(girl.suit_my_taste?).to eq(true)
  end

  # 測試用例3
  # 創(chuàng)建了一個 既單身又漂亮的 女孩, 來驗證 suit_my_taste? 方法
  it "suit my taste? {single but not beautiful}" do
    params = { single: true, beautiful: true }
    girl = Girl.new(params)
    expect(girl.suit_my_taste?).to eq(true)
  end
end
Tips 1:如果只想運行g(shù)irl_spec.rb測試文件中的某一個測試用例,該怎么處理圣拄?

rspec 測試單個用例的命令格式: ruby rspec file_path/file_name:LineNumber 即:加上冒號和it語句塊對應(yīng)的行號嘴秸;
運行如下測試命令:運行g(shù)irl_spec.rb文件中的第3個測試用例:(第3個it ...do 對應(yīng)的行號為23)

rspec spec/models/girl_spec.rb:23

運行結(jié)果如下圖:


解釋:"."代表 該測試用例通過。

Step 5: 優(yōu)化

Tip 2: 為了遵循Ruby的 DRY原則(Don't repeat yourself), 下面介紹一下before庇谆,subject岳掐,let的用法饭耳;

1. before的用法

before(:each)會在每個測試用例執(zhí)行前, (每段it之前執(zhí)行)
before(:all) 會所有測試用例執(zhí)行前串述,只執(zhí)行一次,(整段describe前只執(zhí)行一次)

假設(shè)要為構(gòu)建出的不同實例對象寞肖,測試它們的每個模型方法纲酗,
在此,我選用before(:all)來優(yōu)化代碼:

require 'rails_helper'

RSpec.describe Girl, type: :model do

  before(:all) do
    params_1 = { single: true, beautiful: false }
    params_2 = { single: true, beautiful: true }
    @girl_1 = Girl.new(params_1)
    @girl_2 = Girl.new(params_2)
  end

  ########################################################
  # 測試用例1-1
  # 創(chuàng)建了一個 單身 但不漂亮的 女孩, 來驗證 have_chance? 方法
  it "have chance? {single but not beautiful}" do
    expect(@girl_1.have_chance?).to eq(true)
  end

  # 測試用例1-2
  # 創(chuàng)建了一個 單身 但不漂亮的 女孩, 來驗證 suit_my_taste? 方法
  it "suit my taste? {single but not beautiful}" do
    expect(@girl_1.suit_my_taste?).to eq(true)
  end

  ########################################################
  # 測試用例2-1
  # 創(chuàng)建了一個 單身 但不漂亮的 女孩, 來驗證 have_chance? 方法
  it "have chance? {single but not beautiful}" do
    expect(@girl_2.have_chance?).to eq(true)
  end

  # 測試用例2-2
  # 創(chuàng)建了一個 既單身又漂亮的 女孩, 來驗證 suit_my_taste? 方法
  it "suit my taste? {single but not beautiful}" do
    expect(@girl_2.suit_my_taste?).to eq(true)
  end

end

運行如下測試命令:運行g(shù)irl_spec.rb文件中的所有測試用例

rspec spec/models/girl_spec.rb

運行結(jié)果如下圖:

Paste_Image.png

解釋:".F.."代表第2個測試用例沒通過,其他測試用例均通過新蟆。

2. subject用法

subject { ... } 定義了一個叫做 subject 的方法, 它返回了代碼塊內(nèi)定義的那個對象.
subject可以用來配合should進行隱式調(diào)用觅赊,何為隱式調(diào)用?(見參考鏈接3琼稻,本文暫不講述)
如果你要使用主動式的 expect茉兰,那么可以給subject起名字(非隱式調(diào)用):
在上述girl_spec.rb文件中 添加如下代碼 也可行

# 使用 subject
  subject(:girl_1){ @girl_1 }
  subject(:girl_2){ @girl_2 }

  it "girl_1.have chance?" do
    expect(girl_1.have_chance?).to eq(true)
  end
  it "girl_1.suit my taste?" do
    expect(girl_1.suit_my_taste?).to eq(true)
  end

  it "girl_2.have chance?" do
    expect(girl_2.have_chance?).to eq(true)
  end
  it "girl_2.suit my taste?" do
    expect(girl_2.suit_my_taste?).to eq(true)
  end

3. let的用法

let和before 有區(qū)別,詳見參考鏈接5
在上述girl_spec.rb 文件中 添加如下代碼 也可行

  # 使用let
  let(:girl1){ Girl.new({ single: true, beautiful: false }) }
  let(:girl2){ Girl.new({ single: true, beautiful: true }) }

  it "girl1.have chance?" do
    expect(girl1.have_chance?).to eq(true)
  end
  it "girl1.suit my taste?" do
    expect(girl1.suit_my_taste?).to eq(true)
  end

  it "girl2.have chance?" do
    expect(girl2.have_chance?).to eq(true)
  end
  it "girl2.suit my taste?" do
    expect(girl2.suit_my_taste?).to eq(true)
  end

結(jié)語

是不是不過癮欣簇?大家是否也發(fā)現(xiàn)规脸,要對自己的模型方法進行一個完善的測試,需要構(gòu)建合法的數(shù)據(jù)熊咽,手動構(gòu)建數(shù)據(jù)的量太少莫鸭,無法滿足大量測試數(shù)據(jù)的需求,于是乎横殴,工廠女孩(factory_girl)就要出場了, 另外一個角色就是faker(這個可不是LOL界的faker大魔王), 它能夠構(gòu)建類似真實的數(shù)據(jù)被因,兩者結(jié)合,威力無窮衫仑,欲知后事如何梨与,且聽下回分解...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市文狱,隨后出現(xiàn)的幾起案子粥鞋,更是在濱河造成了極大的恐慌,老刑警劉巖瞄崇,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呻粹,死亡現(xiàn)場離奇詭異壕曼,居然都是意外死亡,警方通過查閱死者的電腦和手機等浊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門腮郊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人筹燕,你說我怎么就攤上這事犁苏∥璐裕” “怎么了愕够?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵挣惰,是天一觀的道長。 經(jīng)常有香客問我糠涛,道長援奢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任忍捡,我火速辦了婚禮集漾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘砸脊。我一直安慰自己具篇,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布凌埂。 她就那樣靜靜地躺著驱显,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瞳抓。 梳的紋絲不亂的頭發(fā)上埃疫,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音孩哑,去河邊找鬼栓霜。 笑死,一個胖子當(dāng)著我的面吹牛横蜒,可吹牛的內(nèi)容都是我干的胳蛮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丛晌,長吁一口氣:“原來是場噩夢啊……” “哼仅炊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起澎蛛,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤抚垄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體督勺,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡渠羞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年斤贰,在試婚紗的時候發(fā)現(xiàn)自己被綠了智哀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡荧恍,死狀恐怖瓷叫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情送巡,我是刑警寧澤摹菠,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站骗爆,受9級特大地震影響次氨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜摘投,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一煮寡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧犀呼,春花似錦幸撕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宋光,卻和暖如春貌矿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背罪佳。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工逛漫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菇民。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓尽楔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親第练。 傳聞我的和親對象是個殘疾皇子阔馋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容