ホーム > タグ > RSpec

RSpec

TDD Boot Campに参加してきました

これまた、割と今更なエントリ。当日は@t_wadaさんのお誘いでRubyグループのコーチ役として参加したけどあまりコーチらしいことしてないな・・・申し訳ない。

TDD Boot Camp(以下tddbc)では、午前中に @t_wada さんと Lasse氏 の講演、午後は各言語ごとにグループを作りペアプログラミング。朝から夜にかけてがっつりなイベントだった。TDDをやりたくてもやり方がわからない、やってみたはいいけど上手くいかないって人にはとてもいいイベントだったんじゃなかろうか。実際、僕も色々と得るものがあった。

とりあえず、当日のお題をあとで自分で書いたものを晒しておく。スレッドセーフ以外の仕様変更まで取りこんである。

http://github.com/ukstudio/LRUCache

これは1つのクラスにまとめてあるけど、結構複雑な感じになってきているので、CacheItemみたいな感じでもう1つ別のクラスを作ってそっちにキャッシュの保存期限とか持たせた方がいいかもしれない。仮にここからクラスを抽出するとしてこれだけテストが書いてあればそんなに苦労せずに抽出できるはず。テストがあるからこそクラスを抽出させるという変更も恐れることなく対応できる。

Fixnum#seconds_laterはLasse氏のを参考にした。個人的にStubの影響をブロックの中だけに限定させたかったので少し修正してある。あとは、Rspecの機能で言うと、subjectやカスタムマッチャを使ってる。それぞれそんなに難しくないので使ってみることをおすすめする。

最後にちょろっと感想を伸べておくと、tddbcはとても素晴しいイベントだったと思う。 @ebackyさんや、来日してくれたLasse氏をはじめ、スタッフのみなさん、今回このようなイベントを開催してくれて本当にありがとうございました。また参加された皆様からも色々な気づきを得られました。ありがとうございます。

僕はTDDでプログラマの階段を更に1歩登れたように思う。ハッキリ言って、TDDを知る前と知った後のコードにはかなりの差がある。なにより、TDDでのプログラミングは楽しい。tddbcに参加された皆様もこの楽しさを知ってもらえればと思う。

RSpecでprivateメソッドをテストする

Object#send(__send__)ならメソッドの呼び出し制限に関わらずメソッドを呼び出すことが可能なので、privateメソッドもテスト可能。

確か、1.9以降はメソッド呼び出し制限がObject#sendにも影響するとどこかで見た記憶があるのだけど結局そうはなっていないみたい。

1.9.1、1.8.7で確認済み。

ちなみにオマケ。

Pythonはメソッド名の前にアンダースコアを2つけるとprivateなメソッドになるのだけれど、実際のところ別名でメソッドを定義してそちらを呼び出してるっぽい。別名で定義された方はprivateではないので、そちらを呼び出してテストすることが可能。

他にもJavaだったらsetAccessible(True)を実行すればpublicなメソッドに変更されるのでテスト可能。と聞いただけで確認はしていない。

RSpecの結果をHTMLで出力する方法

RSpecにはオプション-hがあるので、そこでフォーマットを指定してやればいい。以下、Railsの例。

RAILS_ROOT$ spec spec -f h:spec/spec_report.html

これで、specディレクトリにspec_report.htmlが出力される。

rails.vimのファイル切り替えをRSpecに対応させる(仮)

rails.vimをインストールすると、RcontrollerとかRmodelコマンドでRSpecファイルからControllerやModelファイルに切り替えることができるんだけど、その逆ができないので対応させた。仮なのはVimスクリプトをちゃんと書けなくてコピペのごまかしだから。

書きかえるファイルは~/.vim/autoload/rails.vim。一応行数も書いたけどもしかしたらズレてるかもしれないので参考程度に。あと、バージョンは2.0使ってる。

まずは、:Rspec、:Rspeccontroller、:Rspecmodelを呼び出せるようにする。

1777   call s:addfilecmds("integrationtest")
1778   call s:addfilecmds("spec")
1779   call s:addfilecmds("speccontroller")
1780   call s:addfilecmds("specmodel")
1781   call s:addfilecmds("stylesheet")

なんでわざわざ3つあるのかと言うと、「:Rspec hoge」ってやった時にControllerのスペックなのか、Modelのスペックなのか判断する方法がわからなかったから。だからとりあえず「:Rspec」とやったときは、今開いているファイルに対応するスペックファイルを開き、個別に指定して開きたいときは「:Rspeccontroller hoge」もしくは「:Rspecmodel hoge」で明示的にControllerかModelかを指定するようにした。

次に「:Rspeccontroller [tab]」や「:Rspecmodel [tab]」としたときに、スペック名を補完できるようにfunctionを追加する。

1941 function! s:speccontrollerList(A,L,P)
1942   return s:autocamelize(s:relglob("spec/controllers/",s:recurse,"_controller_spec.rb"),a:A)
1943 endfunction
1944
1945 function! s:specmodelList(A,L,P)
1946   return s:autocamelize(s:relglob("spec/models/",s:recurse,"_spec.rb"),a:A)
1947 endfunction

最後に実際にファイルを開く部分。2331、2334、2340、2345の”spec”、”speccontroller”、”specmodel”が最初に設定したaddfilecmdsの引数と対応するっぽい。なのでfunction名はなんでもいいと思うけど、とりあえず他のに合わせた。

specEditは「:Rspec」に対応していて、開いたファイルがControllerかModelかで開くスペックファイルもかわる。speccontrollerEditとspecmodelEditはControllerかModelどちらか決まったものしか開かない。

2328 function! s:specEdit(bang,cmd,...)
2329   if s:model() != ''
2330     let f = s:model()
2331     return s:EditSimpleRb(a:bang,a:cmd,"spec",f,"spec/models/","_spec.rb")
2332   else
2333     let f = s:controller()
2334     return s:EditSimpleRb(a:bang,a:cmd,"spec",f,"spec/controllers/","_controller_spec.rb")
2335   endif
2336 endfunction
2337
2338 function! s:speccontrollerEdit(bang,cmd,...)
2339   let f = s:controller()
2340   return s:EditSimpleRb(a:bang,a:cmd,"speccontroller",f,"spec/controllers/","_controller_spec.rb")
2341 endfunction
2342
2343 function! s:specmodelEdit(bang,cmd,...)
2344   let f = s:model()
2345   return s:EditSimpleRb(a:bang,a:cmd,"specmodel",f,"spec/models/","_spec.rb")
2346 endfunction

とりあえず、現状の設定で簡易的にRSpecに対応させることができる。「:Rspec」の存在が気持ち悪いのであれば、それは消して開いているファイルがControllerかModelか自分で判断して「:Rspeccontroller」か「:Rspecmodel」を使いわければいいと思う。「:Rspec [tab]」でエラーが出るし個人的には気持ちわるいんだけど、対応させたスペックを開くことの方がおおいし、その時は短いコマンドの方が都合がいいのでここらへんは妥協。今回Viewには対応させていないけれど、同じ要領でできるはず。

rails.vimを少しよんだだけだし、実際の動作もまともに検証してないので変な動作したらごめんなさい。まぁ致命的な問題は起きないだろうけど。正式にrails.vimがRSpecに対応してくれるといいんだけどね。

MacBookにRailsの自動テスト環境を構築した

なんかautotestが流行りつつある今日この頃ですが、みなさまいかがお過ごしでしょうか。とりあえず丁度うちの会社でも自動テストしようかーみたいな話がでてたので構築してみましたよ!

ZenTestのインストール

とりあえずは定番のZenTest(autotest)とRedGreen(結果の色付け)をインストールします。

$ sudo gem install ZenTest
$ sudo gem install RedGreen

次にautotestの設定ファイルの作成。example_dot_autotest.rbのパスは人によって違うと思うので適当に読み替えてください。

cp /opt/local/lib/ruby/gems/1.8/gems/ZenTest-3.9.1/example_dot_autotest.rb ~/.autotest

~/.autotestの15行目のコメントアウトを解除。

require 'autotest/redgreen'

RAILS_ROOTでautotestを実行。起動時に全部のテストを実行するのでちょっとだけ重くなるかもしれません。それ以降はテストファイルやモデル、コントローラファイルなどを修正する度に該当のテストが走ります。

$ cd RAILS_ROOT
$ autotest

実行するテストはtest/unit、もしくはRailsにRSpecが入れてあればRspecのテストを実行します。

なんかspec_serverを立ち上げておくと処理が早くなるらしいですがよくわかってないです。まぁとりあえず設定しといて損はなさそうです。

$ vi spec/spec.opts # 最下行に --drb を追記。drbでテスト用サーバへ繋ぐようになります。
$ ruby script/spec_server

テストの結果は成功なら緑色のバーが、失敗なら赤色のバーが表示されます。

結果通知用にGrowlとScreenの設定をする

これでも自動テストは走るので問題ないですが、結果を見るのがちょっと面倒ですね。ということでGrowlとScreenで結果が見えるようにしましょう。

screenの場合は角谷さんのエントリーを見るといいと思います。基本的にそのまま~/.autotestにコピペするだけで問題ないはずです。

require 'autotest/screen'   # コメントアウトを外す

Autotest::HOOKS.delete(:interrupt)
Autotest::Screen.statusline = %q[|%c %m/%d|%w %=]

class Autotest::Screen
  SCREEN_COLOR[:green] = 'gw'
  SCREEN_COLOR[:yellow] = 'yk'

  Autotest.add_hook :run_command do |at|
    message 'Running' if execute?
  end

  Autotest.add_hook :quit do |at|
    clear if execute?
  end

  Autotest.add_hook :ran_command do |at|
    return unless execute?
    results = [at.results].flatten.join("¥n")
    output = results.slice(/(¥d+)¥sexamples?,¥s*(¥d+)¥s.*failures?(?:,¥s*(¥d+)¥s.*pendings?)?/)
    if output
      ex,fail,pend = $~.captures.map {|e| e.to_i}
      if 0 < fail
        message "FAIL #{ex}ex, F:#{fail} ", :red
      elsif 0 < pend
        message "Pend #{ex}ex, F:#{fail} P:#{pend}", :yellow
      else
        message "All Green #{ex}ex", :green
      end
    end
  end
end

この設定でscreenとautotestを起動しておくと、ステータスバーの右側にテストの結果が表示されます。

autotest

これ貼ってから気づいたけど、Terminalから透けてみえる絵がなんかあれですなw

次にGlowlの設定ですが、Leopardではruby-growlのインストールが必要っぽいです。とりあえずmacportsからインストールしておきましょう。それとGrowlの設定でリモートを許可しておきましょう。

$ sudo port install ruby-glowl

Growl

Growlで結果を通知するにはgrowlnotifyのインストールが必要です。もしインストールしていなければ本家からdmgファイルをDLし、その中にあるinstall.shを実行します。

$ cp -r /Volumes/Growl\ 1.1.2/Extras/growlnotify ~/tmp
$ cd ~/tmp/growlnotify
$ sudo ./install.sh
$ growlnotify -m "hoge" # growlの通知が表示されればインストール完了

~/.autotestを修正し、glowlで結果が通知されるようにします。

require 'autotest/glowl' # コメントアウトを解除

これでテストが失敗したとき、失敗から成功になったときにGrowlが通知してくれます。Growlの見た目を変更しようと思ったけれど、個人的にscreenだけで十分だったので今回はナシ。

とりあえず自動テストはテストを書くのが楽しくなりますな。オススメ。

test/unitで書いたテストにRSpecでテストを追記する方法

10分ぐらい調べて試してみただけだけど。

require 'rubygems'
require 'test/unit'
require 'spec'

class TestArray < Test::Unit::TestCase

  # test/unit
  def test_1
    assert_equal(0, Array.new.size)
  end

  # rspec
  describe Array, "when empty" do
    before do
      @empty_array = []
    end

    it "should be empty" do
      @empty_array.should be_empty
    end

    it "should size 0" do
      @empty_array.size.should == 0
    end

    after do
      @empty_array = nil
    end
  end
end
$ ruby test.rb
....

Finished in 0.007974 seconds

4 examples, 0 failures

RSpecの方のテストはるびまの角谷さんの記事から拝借しました。

spceをrequireして、クラスに書きこんでいけばよさそう。仕事だとtest/unitで書いてしまったテストも多いだろうから、test/unitからRSpecに移行するのにいいかもしれない。

Home > Tags > RSpec

Search
Feeds
Meta

Return to page top