Home > Tags > RSpec

RSpec

TDDBC 東京 1.6でサポートスタッフをやってきました

TDD Boot Camp 東京 1.6にRubyのサポートスタッフとして参加してきた。

当初の予定ではあくまでサポートなのでペアプロには参加しない予定だったけど、Rubyのグループが5人で人数が奇数になってしまったので急遽ペアプロに参加することにした。

この辺は@t_wadaさんや主催の@pocketberserkerさんとも相談してペアとトリオのふたつに分けるっていう話もあったけれど、僕が「せっかくだからペアプロして帰ってもらいたい」と思ったので若干の迷いはあれどペアプロに参加することにしたのでした。Ruby組は@takahashimさんや@1syoさん、@kwappaさんたちが参加されていて彼らなら付きっ切りでサポートしなくても大丈夫そうというのも理由のひとつ。

コードレビューを見た感じだと一応はうまくいったんじゃないかなと思う。特に@1syoさんと@takahashimさんのペア(ダブル高橋!)は積極的に質問をしてくれたので、僕としても教えがいがあった(えらそう)。ただ、質問を受けると僕の方のペアプロが一時的に中断してしまうし、僕としても頭の切り替えが結構大変だったので出来ることならやはり参加者同士でペアが組めるといいんだろうなぁ。理想を言うと全部のペアをスタッフ+参加者にすることなんだろうけど、あまり現実的じゃなさそう。

今回、最初に僕とペアを組んだ方が(お名前を聞くのを忘れてしまった…)モダンなテストの書き方を知りたいと言うので、ある程度xUnit的なテストの書き方でペアプロを進めた後、がっつりRSpec的な書き方にリファクタリングするということをやってみた。他のペアもRSpecは初期のit "なんちゃらかんちゃら"の書き方をされていたので、コードレビュー時に共有できたのはよかったのかな。RSpecの書き方については言っちゃいけないということはないだろうから言っちゃうけどそのうちるびまの方に書く予定なのでお楽しみに。

Ruby組は全体的にTDDのサイクル自体はできていた感じで、内容としてはテストの書き方にフォーカスが移っていたように感じる。例えば、標準出力が絡むテストのやり方とか、システム時刻が絡むテストのやり方とかdescribe/contextの切り方とか。多分、TDDBCも回数が増えていくにつれ、こういう人達がどんどん増えてくると思うからTDDBCの次の道をしめせるといいんじゃないかと思った。参考図書はいくつかあるけど、例えばxUTPとかTDDBCからのジャンプアップがヘビーすぎるなぁという気もする。(もちろんいきなりあれを全部読めという話でもないんだけれど)

話は変わって当日のお題を再度ひとりで実装してみたので、コードを公開しておきます。

ukstudio/tddbc1.6 – GitHub

あまりじっくり書いたって感じでもないので仕様漏れとかありそうだけど、テストコードの書き方の参考にはなるかと思います。@bleisさんみたいに細かに解説しようと思ったけど気力がないのでした。

そういえば今回のスタッフも若手が多いな。@bleisさんと俺が86世代、@kyonn_mmさんと@pocketberserkerさんがひとつだかふたつだか年下だっけ? 同世代の人が頑張ってるのはとても刺激になります。

最後に、主催の@pocketberserkerさん、講演をしてくださった@t_wadaさんをはじめ、スタッフの方々どうもお疲れ様でした。参加者のみなさま、どうもありがとうございました。

特に@pocketberserkerさんは佐賀の人なのにもかかわらず東京でTDDBC主催とか、そのパワフルさを見習いたい感じです。本当にお疲れ様でした。

おまけ

RSpec日本語化計画

やっぱ僕達は日本人なので仕様記述的と言うからには日本語で書きたい。とりあえずRubyは日本語を一応識別子に使えるのでその辺で適当にやってみた。shouldが鬼門すぎるのだがどうしたもんか。「it should_not 空であること」って明らかにおかしすぎる。@takaiさんが少し修正してくれた。

本当はもっとちゃんとした記事を書くつもりだったんだけどなんか脇道にそれてしまった。

RSpec2+Rails3+autotest環境の構築

9月からやる仕事がめでたく、Rails3.0 + Ruby1.9.2のお仕事なので色々と環境構築。とりあえず自動テストまわりやりました。

一応、環境を他と切り分けるために、rvmでアプリ用にgemsetを用意。アプリごとに簡単に環境を構築できるrvmマジ便利。

gem install bundler --pre
gem install rails

まずは、bundlerとrailsをインストール。次は適当なアプリを作って必要なgemのインストールなどを行う。テストはRSpecで書くので、-TをつけてTest/Unitは使わないようにする。

rails new demo -T

次に必要なgemをGemfileに記述。rspecとかを「テストだけだから」と思って、gropu :testにしたら、モデルを作成したときにTest/Unitのテストコードが作られたりしたので注意。githubのWikiを見るとautotestのgemは不要そうなんだけど、実際ないとうまくautotestが動かなかた。

bundle install

あとはモデルを作って、テストが実行できればOK。

rails g rspec:install
rails g model user
rake
autotest

追記

Twitterで@conceal_rsさんから補足ありました。ありがとうございます!

@ukstudio ZenTestとautotest-railsはなくても大丈夫ですよ。あとrspecは2.0.0.beta.20からwebratに依存しなくなったのでhave_selectorとか使えなくなってます。gem ‘webrat’もあった方がいいかもless than a minute ago via Termtter

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

Feeds
Meta
Others

Return to page top