連載
» 2008年11月26日 00時00分 公開

RSpecを使ったテストコードを読もうRailsコードリーディング〜scaffoldのその先へ〜(2)(4/4 ページ)

[倉貫義人、松村章弘,TIS株式会社/SonicGarden]
前のページへ 1|2|3|4       

コントローラのスペックを読んでみよう

 モデルのスペックでは、メソッドを呼び出して、その戻り値を確認するという単純な動きの確認で済みましたが、コントローラ部分のスペックについては、そうはいきません。RSpecではRails用の拡張として、コントローラの行う、画面からの処理の流れを確認することができるようになっています。その場合のスペックの記述は、モデルと少し流れが変わります。

 では、第1回で紹介したGroupsControllerのindexアクションを例に説明していきましょう。

24 def index
25   params[:yet_participation] ||= false 
26   params[:group_category_id] ||= "all" 
27   params[:sort_type] ||= "date"
28   @format_type = params[:format_type] ||= "detail"
29   @group_counts, @total_count = Group.count_by_category
30   @group_categories = GroupCategory.all
31 
32   options = Group.paginate_option(session[:user_id], params)
33   options[:per_page] = params[:format_type] == "list" ? 30 : 5
34   @pages, @groups = paginate(:group, options)
35 
36   unless @groups && @groups.size > 0
37     flash.now[:notice] = '該当するグループはありませんでした。'
38   end
39 end
   app/controllers/groups_controller.rb 24-39

 このindexというアクションは、グループの一覧を表示するためのアクションです。

 Railsのコントローラの中でやるべきことは、大きく分けて以下の3つになります。

  1. パラメータを解析し、モデルに処理を渡す
  2. インスタンス変数(@マークの変数)や、flashに値を設定する
  3. 表示するviewを設定する/リダイレクトの場合はリダイレクト先を設定する

 GroupsControllerのindexアクションで見てみましょう。

 モデルに処理を渡している部分は、少し分かりにくいですが、34行目のpaginateメソッドです。このメソッドは、ページ遷移をするための処理で、この内部でGroupモデルにパラメータを渡してデータベースの検索を行っています。

 インスタンス変数に関しては、28行目から34行目までで設定されています。flashは、必ずしも設定する必要はありませんが、今回は、検索結果に何も表示すべき項目がない場合、37行目でそのむねのメッセージを設定しています。

 表示するviewは、ここでは何も指定していません。Railsの規約で、viewを指定していなければ、アクション名と同じviewが設定されることになっています。よって、index.html.erbというviewを表示することになります。

 では、スペックを見てみましょう。ファイルは、モデルのときと同じでspecディレクトリに、_specを付けたファイル名になっています。

18 describe GroupsController do
19   before do
20     user_login                                     # ログイン状態にしている 
21   end
22   describe "#index" do
23     describe "グループが存在した場合" do
24       before do
25         @pages = mock('pages') 
26         @groups = mock('groups', :size => 1)
27         controller.should_receive(:paginate).and_return([@pages, @groups])
28         get :index
29       end
30 
31       it { response.should be_success }
32       it { assigns[:format_type].should == "detail" }
33       it { assigns[:group_counts].should == Group.count_by_category.first }
34       it { assigns[:total_count].should == Group.count_by_category.last}
35       it { assigns[:pages].should == @pages }
36       it { assigns[:groups].should == @groups }
37     end
38     describe "グループが存在しなかった場合" do
39       before do
40         @pages = mock('pages')
41         @groups = mock('groups', :size => 0)
42         controller.should_receive(:paginate).and_return([@pages, @groups])
43         get :index
44       end
45 
46       it { response.should be_success }
47       it { flash[:notice].should_not be_nil }
48     end
49   end
50 end
   spec/controllers/groups_controller_spec.rb 18-50

 コントローラのスペックは、前述のモデルの例より、少し複雑になっています。しかし、基本的な要素は、describe、before、itの3つなのは変わりありません。それでは、順に見てみましょう。

 18、22、23行目でdescribeが存在します。ネストした状態になっていますが、このことは順番に前提条件が適用されていくことを示しています。23行目のdescribeの中で実行されるitでは、GroupsController(1つ目のdescribe)でindexアクション(2つ目のdescribe)にグループが存在した場合(3つ目のdescribe)という前提条件が設定された状態で実施されることになります。

 また、コントローラのスペックのための新しい要素として28行目にgetというメソッドを使っています。これは、Rails用のRspecの拡張で、GroupsControllerのindexアクションに疑似的にHTTPのGETリクエストを送るということを示しています。Webブラウザから/groups/indexにアクセスがあったときと同じ状況を作り出してくれます。

 3つの前提条件の状態をそれぞれのbeforeブロックで設定し、31行目からの検証のitのブロックに入っていきます。先ほど説明したコントローラのやるべきことができているか検証します。

 31行目のresponseは、先ほどのgetメソッドで呼び出したリクエストのレスポンスに対応する結果にアクセスするメソッドです。そのレスポンスが、成功していることつまり、正しくrenderされたことを検証しています。

 32行目から36行目でインスタンス変数に値が設定されていることを検証しています。assignsメソッドでは、getメソッドで呼び出されたときにインスタンス変数に設定された値にアクセスするメソッドです。

 38行目から48行目までは、グループが見つからない場合のスペックになっています。ここまで読んだ読者であれば、どういったスペックかは説明をしなくても読めるようになっているのではないでしょうか。


 コードリーディングの連載で、なぜRSpecを取り上げたのでしょうか。それは、Railsアプリケーションのソースコードを理解するうえで、RSpecのコードを読めることが、大変大きな助けになるからなのです。

 今回のソースコードでも分かるとおり、スペックのコードと実際に動くコードは、裏返しになっています。スペックを読むことで、Railsアプリケーションの振る舞いを理解することができるのです。

 RSpecのように、スペックを記述するためにRubyのオリジナルの文法を拡張してできた独自の言語のことを、DSL(Domain Specific Language:ドメイン特化言語)と呼びます。RSpecは、テストコード(スペック)を書くためのDSLというわけです。

 ある意味、新しい言語なので、最初のハードルは少し高いかもしれませんが、一度覚えてしまえば、ルールに従って読み書きできるので、むしろ分かりやすくなります。今回の記事が読者の皆さまにとっての最初のハードルを越えるきっかけになれば幸いです。

前のページへ 1|2|3|4       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。