月曜日までに考えておきます

ITネタとゲームネタ中心に興味のあること色々書きます。

Shibuya.rb参加してきてRSpec改善しようと思った話

昨日、渋谷のRuby勉強会、「Shibura.rb」に参加してきました。 いろいろと役に立つ発表が聞けたのですが、とくに @r7kamura さんのTest on Railsの話が参考になりました。 この発表が起点となって、優れたRubyist達のRSpecの書き方に関して議論が巻き起こってたのですが、
  • Modelのテストはしっかり書く
  • Controllerのテストはそんなに書かない
  • before(:each) とか使わない
  • subject使う
みたいなのがどうも流行りみたいで、全部自分がやってるのと真逆だなぁと痛感させられました。 Modelのコードはテスト/プロダクションともに少ない、Controllerのテストがっつり、before(:each)使いまくり、subject使ってない、でした(´・ω・`) というわけでさっそく今日、subjectを使ってeachを使うのをやめてみるのにトライ。 名前が必須なTeamというModelのテストで、名前がnullだとエラーになることの検証をするのにこういうコードを書いてたのですが、
let(:team){Team.new}
before(:each) do
  team.name = nil
end
it "should_not be_valid" do
  team.should_not be_valid
end
これはこんな感じで書けますね。(※この記事内のテストコードはdescribeとかcontextは省略してます)
subject{Team.new}
it{should_not be_valid}
とはいえ、上のコードは明示的にnameにnilを入れていますが下のコードはnilを入れていないのでやっていることが違いますね。 このTeamは生成したときにnameがnilになるので下の書き方でもテストが通るのですが、明示的にnilを入れたい場合どうすればいいのかなぁ、というのが悩み。
subject{
  team = Team.new
  team.name = nil
}
it{should_not be_valid}
こう書いた場合、subjectは{}内の戻り値を値に取るので、この場合だとteam.name = nilの戻り値、nilになってしまってitが対象にするのがTeamのインスタンスではなくなりテストが通らなくなるんですよね。これを防ごうとすると、
subject{
  team = Team.new
  team.name = nil
  team
}
it{should_not be_valid}
という書き方であれば通りますが、なんかこれだとbefore(:each)使うパターンと行数が変わらないので他にいい方法があるのかな、というのが気になっているところ。 他にも、teamは必須項目として同じくowner_idを持つとした場合、エラーがowner_idが空であることに起因せず、nameが空なのでエラーであることを検証しようとすると、before(:each)を使うなら以下の様な書き方になると思います。
let(:team){Team.new}
before(:each) do
  team.name = nil
  team.owner_id = 1
end
it "should_not be_valid" do
team.should_not be_valid
end
これをsubjectを使うのであれば
subject{
  team = Team.new
  team.name = nil
  team.owner_id = 1
  team
}
it{should_not be_valid}
letも組み合わせるなら
let(:team){
  team = Team.new
  team.name = nil
  team.owner_id = 1
  team
}
subject{team}
it{should_not be_valid}
みたいな書き方になるのかな、と思いました。 で、一番最後のインスタンス返すために team だけの行がイマイチなのとそもそもbefore(:each)の場合と可読性/行数共にあまり変わらない気がしたので、他の人達はどういう書き方をしているのかな、というのが今すごく気になっているところだったりします。