構建一個 Ruby Gem 第三章 配置 測試/調試 環(huán)境

配置 測試/調試 環(huán)境

一本沒有測試相關內容的 Ruby 的書不是完整的舔株。如果你對發(fā)布和貢獻開源項目感興趣的話威酒,社區(qū)會更嚴肅的對待你的代碼如果它們是被測試覆蓋的并且測試通過的話鹤竭。

測試驅動開發(fā)(TDD) 是一種實戰(zhàn),在你寫代碼之前先寫測試胎源。實踐 TDD 幫助我們只寫必要的能讓測試通過的代碼耻卡。這也能減少過度工程的可能性(注意到這里有一個模式了嗎?)

在 ??Ruby 測試社區(qū)有兩種觀點吉执。一種喜歡 Minitest (Ruby標準庫內置的), 另一個更喜歡 Rspec。我喜歡后者并且每天都使用 Rspec塔拳。我發(fā)現(xiàn)它很適合我而且我喜歡用 DSL 來組織我的測試鼠证。

依賴


為了加入 Rspec,讓我們打開 mega_lotto.gemspec 文件然后加入下面的依賴:

 spec.add_development_dependency "rspec"

注意: 由于我們不想強制讓 rspec 被我們的宿主應用加載靠抑,我們使用 add_development_dependency 方法量九,而不是 add_dependency

現(xiàn)在, 讓我們切換到終端颂碧,在我們的 gem 的根目錄下運行:

$ bundle install

這會安裝在 gemspec 里列出的所有的依賴(包括 Rspec)荠列。輸出結果就像下面那樣:

Fetching gem metadata from https://rubygems.org/.........
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Resolvingdependencies...
Using rake (10.1.0)
Using bundler (1.3.5)
Using diff-lcs (1.2.5)
Using mega_lotto (0.0.1) from source at . Using rspec-core (2.14.7)
Using rspec-expectations (2.14.4) Using rspec-mocks (2.14.4)
Using rspec (2.14.1)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

不要過于在意版本號,它們會經常更變载城。正如你所看到的肌似,bundler 安裝了 rakerspec(由多個 gem 組成)诉瓦,和我們的 gem, mega_lotto川队。
為了完成 Rspec 的安裝,在 gem 的根目錄下運行下面的命令:

$ rspec --init

輸出的結果如下:

create   spec/spec_helper.rb
create   .rspec

一個 spec/ 目錄在我們的項目中被創(chuàng)建睬澡,并且里面有一個 spec_helper.rb 文件固额。

我們需要在 spec_helper 中加點東西。因為我們想要測試我們的 gem 的代碼煞聪,我們需要從 spec_helper.rb 中加載它: 在 spec/spec_helper.rb 文件的頭部加上 require "mega_lotto"

require "mega_lotto"
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# Require this file using `require "spec_helper"` to ensure that it is only
# loaded once.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
  config.treat_symbols_as_metadata_keys_with_true_values = true 
  config.run_all_when_everything_filtered = true 
  config.filter_run :focus
  # Run specs in random order to surface order dependencies. If you find an 
  # order dependency and want to debug it, you can fix the order by providing 
  # the seed, which is printed after each run.
  # --seed 1234
  config.order = 'random'
end

注意:不同版本的 rspecspec_helper.rb 中生產的內容也可能不同斗躏。如果你使用了不同版本的 rspec,你的 spec_helper.rb 文件可能看上去不一樣昔脯。

現(xiàn)在啄糙,我們可以在命令行運行 rspec spec 然后得到下面的信息:

No examples found.
Finished in 0.00007 seconds
0 examples, 0 failures

注意:rspec 是用來運行 rspec 測試的命令,不內涵云稚。它接受文件路徑作為參數(文件或目錄)來確定要執(zhí)行哪個測試隧饼。這確保了我們的測試基礎設施被正確的設置了。

通常來說静陈,spec/ 目錄(當使用 rspec 時我們的測試被安置的地方)燕雁,是我們的 gem 的 lib/ 目錄的鏡像。我們之后會看到這是如何工作的, 假如模塊 lib/meag_lotto/drawing.rb 被加入到我們的 gem 中, spec/mega_lotto/drawing_spec.rb 將是相符的 spec 文件.

正如我們上面看到的窿给,gem 可以有依賴贵白。有時候當你安裝一個 gem 時率拒,幾個其他的 gem 也會被安裝崩泡。這是因為被定義在 gemspec 里的依賴。如果你以前在一個 rails 應用中使用 unicorn gem猬膨,你可能注意到安裝 unicorn 導致 gemfile.lock 中多了幾行角撞。這幾行就是在 unicorngemspec 中定義的依賴呛伴。

Rake 任務


為了運行通過 rspec spec/ 命令來執(zhí)行我們的 specs,我們可以更新我們的 Rakefile 來包含一個 spec 任務并且設置為默認:

require "bundler/gem_tasks"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)

task :default => :spec

現(xiàn)在我們可以使用我們的終端來運行 rake 來看看和上面一樣的輸出:

$ rake
No examples found.
Finished in 0.00007 seconds
0 examples, 0 failures

我們要加入的另一個任務是一個快捷鍵來進入一個終端會話谒所。如果你熟悉 Rails热康,你應該知道 rails c 是一個很牛逼的工具。我們可以給我們的 gem 類似的功能劣领。如果我們的系統(tǒng)中有 Ruby姐军,我們可以打開一個命令行使用 irb 命令來進入一個 Ruby 交互環(huán)境:

$ irb
irb(main):001:0> 2 + 2 => 4
irb(main):002:0> exit

非常好,只不過解釋器沒有加載任何 Ruby 標準庫以外的東西尖淘。這對我們沒什么幫助奕锌,但是幸運的是 irb 命令接受的一些參數可以幫我們一些忙:

$ irb --help
Usage:  irb.rb [options] [programfile] [arguments]
  -f            Suppress read of ~/.irbrc
  -m            Bc mode (load mathn, fraction or matrix are available)
  -d                Set $DEBUG to true (same as `ruby -d')
  -r load-module    Same as `ruby -r'
  -I path           Specify $LOAD_PATH directory
  -U                Same as `ruby -U`
  -E enc            Same as `ruby -E`
  -w                Same as `ruby -w`
  -W[level=2]       Same as `ruby -W`

-I 參數允許我們加入一個特定的目錄到 Ruby 的 load path。

記得嗎當我們討論 lib/mega_lotto 目錄時看到的 mega_lotto.gemspec 文件的頭部

...
lib = File.expand_path('../lib', ___FILE___)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 
...

當 bundler 加載我們的 gem 時村生,它也加載了 lib/ 目錄, 所以我們可以使用我們的庫惊暴。

所以通過使用 -I,我們可以指定 lib/目錄來保證 irb 可以使用我們的代碼趁桃。

另外辽话,在通常狀況下,我們看到 irb 不會加載 Ruby 標準庫以外的庫卫病。所以即使我們加上了我們的 lib/ 目錄到 load path油啤,我們不得不指定調用 require "mega_lotto" 來加載我們的代碼當會話開始時。所以忽肛,選項 -r 被我們使用了村砂。它允許我們當會話開始時去加載一個指定庫,所以我們不用手動去做這件事了屹逛。

把這些參數組合起來我們就得到了一個牛逼的工具來檢驗我們的 gem 的代碼:

$ irb -r mega_lotto -I ./lib
irb(main):001> MegaLotto 
=> MegaLotto

我們可以看到 MegaLotto 模塊在使用合適的參數的irb會話中被使用了础废。

更進一步,我們可以把這行命令加入到我們的 Rakefile罕模,這樣我們就能更容易的調用了评腺。

require "bundler/gem_tasks"
require "rspec/core/rake_task"

RSpec::Core::RakeTask.new("spec")

task :default  => :spec

task :console do
  exec "irb -r mega_lotto -I ./lib"
end

這讓我們可以快捷的從命令行運行 rake console

$ rake console

irb(main):001:0> MegaLotto
=> MegaLotto

調試

無論我們怎么不愿意承認,我們沒有寫出完美的代碼淑掌。Ruby 有很多調試工具蒿讥,但是 pry 是我的選擇。
既然 mega_lotto.gemspec 負責根據環(huán)境加載依賴抛腕,我們可以加入 pry 到開發(fā)列表中:

...
spec.add_development_dependency "bundler", "~> 1.3"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"
spec.add_development_dependency "pry"

運行 bundle install 我們可以看到 pry gem 被列出來了:


Resolving dependencies...
Using rake (10.1.0)
Using bundler (1.3.5)
Using coderay (1.1.0)
Using diff-lcs (1.2.5)
Using mega_lotto (0.0.2) from source at . Using method_source (0.8.2)
Using slop (3.4.7)
Using pry (0.9.12.3)
Using rspec-core (2.14.7)
Using rspec-expectations (2.14.4)
Using rspec-mocks (2.14.4)
Using rspec (2.14.1)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

注意:pry gem 已經安裝在我的系統(tǒng)中了芋绸,所以在輸出中有 "Using pry (...)". 如果 pry 之前沒有被安裝,輸出應該是 "Installing pry(...)"

現(xiàn)在 pry 安裝好了担敌,我們需要去加載它摔敛。正如前面所提到的,lib/mega_lotto.rb 文件是加載其他代碼到 gem 里的主要入口全封。

通常情況下马昙,我們可以在頭部引入 pry桃犬。然而,記住我們只在開發(fā)環(huán)境下使用 pry行楞。這意味著當我們的 gem 被宿主應用加載時攒暇,它會嘗試去加載 pry,有可能會因為沒有 pry 而拋出 Ruby LoadError 異常子房。

知道了這個可能發(fā)生的異常形用,我們可以使用 rescue 然后不做任何處理:

# lib/mega_lotto.rb

require "mega_lotto/version"

begin
  require "pry"
rescue LoadError
end

module MegaLotto
end

注意我們是如何使用 rescue LoadError 代碼塊來捕獲異常的,但是現(xiàn)在我們不做任何處理证杭。 如果不這樣做尾序,LoadError 異常就會被拋出然后我們的代碼就不能被執(zhí)行下去了。

一旦 pry 被加載躯砰,我們就可以使用 binding.pry 方法來停止代碼在那個點上并且開啟一個 REPL 會話來調試每币。讓我們在模塊里加入:

require "mega_lotto/version"

begin
  require "pry"
rescue LoadError 
end

module MegaLotto 
  binding.pry
end
$ rake console
Frame number: 0/7
From: /Users/bhilkert/Dropbox/code/mega_lotto/lib/mega_lotto.rb @ line 12 :
     7:   require "pry"
     8: rescue LoadError
     9: end
     10:
     11: module MegaLotto 
     => 12: binding.pry
     13: end
[1] pry(MegaLotto)>

完美!

注意: 使用 exit 命令來退出 pry 會話琢歇。

我們就不繼續(xù)下去了兰怠,我們會從入口文件移除 binding.pry,但是保留加載的代碼這樣我們以后還可以使用:

require "mega_lotto/version"

begin
  require "pry"
rescue LoadError 
end

module MegaLotto 
end

使用 pry 的詳細內容不是本書的范圍李茫。這是一個牛逼的 gem 并且值得去探索如果你經常使用 Ruby揭保。

我們就到此為止了,因為我們的測試框架和調試工具都已經被正確的安裝和配置了魄宏。

總結

我們在本章花費了時間來配置很多工具秸侣。它們并不都是必要的,但是在我的開發(fā)過程中我發(fā)現(xiàn)了它們的價值宠互。一旦你多試幾次這個過程味榛,就只需要花上幾分鐘就能完成。有了這些工具能幫助你解決一些很麻煩的 bug予跌。

如果你更喜歡 minitest 而不是 rspec搏色,配置起來會容易些因為 minitest 已經內置在 Ruby 標準庫中了,所以不需要額外的 gem券册。

最后频轿,gem 的依賴會很快的變得復雜。如果一個依賴的定義沒有被維護烁焙,就可能給你留下很多 bug 和坑航邢。當然,依賴是有價值的骄蝇,沒有理由去復制功能如果已經有了可靠的解決方案膳殷。只要知道,隨著你增加依賴乞榨,你的 gem 的復雜度就會上升秽之。

在下一章,我們會使用測試驅動的方式來研究和探索 Ruby 的命名空間是如何管理 gem 的文件結構的吃既。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末考榨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鹦倚,更是在濱河造成了極大的恐慌河质,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件震叙,死亡現(xiàn)場離奇詭異掀鹅,居然都是意外死亡,警方通過查閱死者的電腦和手機媒楼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門乐尊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人划址,你說我怎么就攤上這事扔嵌。” “怎么了夺颤?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵痢缎,是天一觀的道長。 經常有香客問我世澜,道長独旷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任寥裂,我火速辦了婚禮嵌洼,結果婚禮上,老公的妹妹穿的比我還像新娘封恰。我一直安慰自己咱台,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布俭驮。 她就那樣靜靜地躺著回溺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪混萝。 梳的紋絲不亂的頭發(fā)上遗遵,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音逸嘀,去河邊找鬼车要。 笑死,一個胖子當著我的面吹牛崭倘,可吹牛的內容都是我干的翼岁。 我是一名探鬼主播类垫,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼琅坡!你這毒婦竟也來了悉患?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤榆俺,失蹤者是張志新(化名)和其女友劉穎售躁,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體茴晋,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡陪捷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了诺擅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片市袖。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖烁涌,靈堂內的尸體忽然破棺而出凌盯,到底是詐尸還是另有隱情,我是刑警寧澤烹玉,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布驰怎,位于F島的核電站,受9級特大地震影響二打,放射性物質發(fā)生泄漏县忌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一继效、第九天 我趴在偏房一處隱蔽的房頂上張望症杏。 院中可真熱鬧,春花似錦瑞信、人聲如沸厉颤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逼友。三九已至,卻和暖如春秤涩,著一層夾襖步出監(jiān)牢的瞬間帜乞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工筐眷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留黎烈,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像照棋,于是被迫代替她去往敵國和親资溃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容