2012年3月22日木曜日

DEPRECATION WARNING:the handler name or pass render :handlers => [:erb] instead



なにやら怪しいメッセージ


[root77@localhost mytestapp]$ rake spec:views

**DEPRECATION WARNING: Passing a template handler in the template name is deprecated. You can simply remove the handler name or pass render :handlers => [:erb] instead. (called from block (2 levels) in <top (required)> at /home/root77/app/mytestapp/spec/views/blogs/index.html.erb_spec.rb:13)
.**


▼ try①

・ addind following gem to Gemfile. And do `bundle install` .

gem 'activeadmin'


: the result of try①  was  NG!!


so,


▼ try②

・ adding following config to config/environments/test.rb or development.rb

config.reload_classes_only_on_change = true
config.watchable_dirs[File.join(config.root, "app/admin")] = ["rb"]


: the result of try②  was  NG!!

so,

▼ try③

・ changing following description

#spec/views/blogs/index.html.erb_spec.rb

require 'spec_helper'

describe "blogs/index.html.erb" do 
describe "blogs/index" do
   ・・・
end


: the result of try③  was  OK!



1時間お疲れ様でした。!


2012年3月12日月曜日

Mock(モック)とは、Stub(スタブ)とは。 まとめ

少し頭の中で整理できたので、まとめおく。
どちらも同じような動作をできてしまうため混同してしまうが、明確に役割を区別し、動作を限定させることが重要である。



▼ 共通

・ 代理の役割を果たす

ex) court(裁判官)オブジェクトが、①{ judgeメソッドを実行すれば false を返す } ことを期待する


: judgeメソッドの中身

judge (){

  h_result = ヒアリングメソッド();
  result = リサーチメソッド(h_result);

  return result;




①の箇所の役割を代理で行うということ


▼ スタブ

スタブの役割は、
インターフェイスとして「確かな結果を出す(false を出す)」という役割のみを期待される。
上記の例からだと、①が期待できれば良いということ。
judgeメソッドがどような課程を経て false という結論に到達したかは、考慮にいれる必要がない場合にスタブを利用する



▼ モック

モックの役割は、
インターフェイスとして「確かな課程を踏み」、「確かな結果を出す(false を出す)」という役割を期待される。
上記の例中であれば、judgeメソッド の中の処理項目(リサーチメソッド、ヒアリングメソッド)も確かに実行されていること、かつ結果としてfalse を返すことを代理実行する役がモックである


2012年3月5日月曜日

[rails3]「Unitテスト」と「rspec」 根本的な違いとは

Unitテストとrspecの違いについて見解を述べたい。
そこには部分的ではあるが、明確な思考過程の違いがあると私は思う。

その思考過程の相違により、アプリケーションの規模が大きくなるにつれ、
潜在的なリスクを埋め込む可能性が大きくなる。

だから私はrspecを選らんだ。



  • 思考過程の違いとは

Unitテストの場合
検証したいことを優先的に考え、その後、その検証方法に従えばそのオブジェクトの動作結果がどうなるかということ(オブジェクトの動作)を最後に結論として宣言するプロセス。

rspecの場合は、
オブジェクトの動作・状態変化を優先的に考え、その後、その動作がどうなることが正しいのか(検証項目)を最後に結論として宣言するプロセス。


つまり、動作を先に考えるのか、検証項目を先に考えるのかという相違がある。



上記は絶対的ではなく、あくまでその傾向があると言いたい。


David Chelimsky著のThe RSpec Bookの書籍が非常に理解を助けてくれたので紹介しておく




上記の主張を以下で説明したい。

★ Unitテスト

class BlogTest < ActiveSupport::TestCase
  test "blog_validate" do  
    assert_equal blog.errors.size, 2, 'Failed to validate count'
    assert blog.errors[:user_id].any?, 'Failed to :user_id validate'
 end
end

★ rspec

describe BlogsController do
  describe "#index" do
    context "when logined_user" do
        subject { response }
          it { should render_template("layouts/#{@crtl_head}") }
        end
    end
  end
end



(まだ、rails・rspecについては触りはじめのため、書き方について至らぬ点はご容赦いただきたい。)

上記の赤文字(特に)の記述が「思考過程の違い」の顕著な例を表していると思う。
その次の違いが青文字部分だ。 青文字 部分は代替手段が適応可能なので、少しの違いと表現しておく。


ではテストコードを書くときどのような思考の過程を経るのか順を追ってみよう。


----------------------------------------------------------------------

①(主題)何についてテストするのかを考える

:これは両者共通の書き始めである。Unitテストは class BlogTest の箇所。rspecはdescribe Blog・・・の部分。そもそもテストするにはテストしたい対象を考えなくてはいけないから当然である。

----------------------------------------------------------------------

※ 極論ここから分岐する
②Unitテストの場合
その主題について、何をテストしたいかを考える。
上記記述のtest "blog_validate" の部分だ。(ex : 私はBlogユニットのvalidateがうまくいくかを検証しよう)
Unitテストの場合、対象を定めた後すぐにどんな検証をしたいか(検証内容)を考えるはめになってしまう。
(ただし、このコードが極端なだけなので、書き方次第ではrspecと同等の思考過程を経ることは可能)

<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>

②rspecの場合
その主題について、どんな振る舞いをするのかを考える、また、どんなコンテキストが考えられるのかを考える。

  describe "#index" do
    context "when logined_user" do
この部分である。(ex : blog_controllerの振る舞いはindexアクションがあり、loginしてるかどうかで振る舞いが変わるね)
rspecの場合は、まだ検証内容を検討しない。そのオブジェクトの振る舞いや状態変化を考えなくてはならい。

----------------------------------------------------------------------

(ここが決定的である。)
③Unitテストの場合
上記考えた検証内容にもとづき、オブジェクトの動作・状態変化を考える。
 assert_equal blog.errors.size, 2
   assert blog.errors[:user_id].any?

記述順としては、assert_equal 一致すると断言しよう (検証内容)、blog.errors.size, 2
ブログのエラー数が2つになることを(オブジェクト動作)、という順となる。

ほかにもUnitテストには

assert_generates XX (生み出すことを断言しよう、XXとなることを)
assert_recognizes XX (認識すると断言しよう、XXとなることを)

というような、検証内容を優先して考え、そのおしりにオブジェクトの動作をひもづける傾向があると私は思う。


<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>


③rspecの場合
上記考えたアクションとコンテキストにもとづき、最終的なオブジェクトアクション(subject { response })を考え、そのアクションがどうなるのかを最後に示す。(render_template("layouts/#{@crtl_head}")
つまり、オブジェクトの動作を隅々にとらえたのちに、最後に検証内容(ex : レンダーすること)を紐づけていく。(検証というよりは動作に重きを置く)


以上。


この最後③(特に緑文字の箇所=検証したいオブジェクトの最終的なアクション・状態の位置)がどうにもならない違いである。

プログラムしていく上で、最後にそれを記述するからには自然に最後にそれを考える思考となってしまう。


検証内容だけを頭から考えたところで、そもそもオブジェクトの振る舞い・状態変化を捉えなければ適切な検証を洗い出すことはできないと私は考え、rspecを選んだ。


おそらく青文字の箇所は、書き方次第でどうにかなる。

(しかし、主題やサブ主題、コンテキストを明確にテストコードに埋め込めるrspec がより有利のではないだろうか。Unitテストは3時間ほどしか触っていないので、誤認であれば申し訳ない)



上記はテストツールの触りはじめの所感である。







2012年3月3日土曜日

rspecでruby on rails3(devise)のcontrollerをいい感じに書いてみた~part2


前回の投稿の続きであるが、
さらに、GETアクションに加え、CREATE・UPDATEアクションも考慮して汎用ロジックを組んでみました。

前回のエントリも参考にしていだだきたい。
[ rspecでruby on rails3(devise)のcontrollerをいい感じに書いてみた~part1 ]

【環境】
devise'2.0.4'
rails'3.1.3'
rspec-rails'2.8.0'

【テスト概要】

アクションごとに

ログインしている場合
・取得オブジェクトが参照できる

:renderingを期待している場合
  ・responseが200[OK]である
  ・正しいtemplateをレンダーする
:redrectを期待している場合
   ・正しいリダイレクト先へいく

ログインしてない場合
   ・取得オブジェクトが参照できない
   ・正しいリダイレクト先へいく

をテストすることを想定。








describe BlogsController do

  before(:each) do
    @crtl = described_class.to_s.downcase
    @crtl_head = @crtl.split(/controller/)[0]

 
    def do_req(req,action,params)
      case req
        when "get" then
          get action, params
        when "post" then
          post action, params
        when "put" then
          put action, params
        when "delete" then
          delete action, params
      end
    end
  end

  describe "#login_user" do
    login_user    
    it { subject.current_user.should be }
  end

  shared_context "Do_Reqest" do |req,action,params,expects,objs,result,template,rto_path|
    before do
        @len = 2
        @len = @len- 1 if action == :index    
        @obj = @crtl_head[0..@crtl_head.length-@len]
    end

    context "in logined_user" do
      login_user
      before { do_req(req,action,params) }
       
      if objs.nil?
        it { assigns(@obj.to_sym).should be ,"assigns(#{@obj.to_sym})"}
      else
        objs.each do |obj|
          it { assigns(obj).should be , "assigns(#{obj})" }
        end
      end

      case result
        when "true" then
          it { assigns(:result).should be_true , ":result=>#{assigns(:result)}"}
        when "false" then
          it { assigns(:result).should_not be_nil }
          it { assigns(:result).should be_false , ":result=>#{assigns(:result)}"}
      end

      case expects
        when "render" then
          it { response.should be_success }
          subject { response }
            it { should render_template("layouts/#{@crtl_head}") }
            it "render template" do
              template = @crtl_head + "/" + action.to_s.delete(":") if template == nil
              should render_template(template), "template=>#{template}"
            end
        when "redirect" then
          if rto_path == nil
            it { subject.response.should redirect_to(blog_path(assigns(:blog))),"redirect_to=>#{blog_path(assigns(:blog))}" }          
          else
            it { subject.response.should redirect_to(eval(rto_path)), "rto_path=>#{rto_path}"}
          end
      end
 
    end
 
    context "in no_logined_user" do
      before { do_req(req,action,params) }

      it { subject.current_user.should be_nil }

      if objs.nil?
        it { assigns(@obj.to_sym).should be_nil ,"assigns(#{@obj.to_sym})"}
      else
        objs.each do |obj|
          it { assigns(obj).should be_nil , "assigns(#{obj})" }
        end
      end
      it { response.should redirect_to(user_session_path) }    
    end
  end


  describe "#index" do
    include_context "Do_Reqest","get",:index
  end

  describe "#new" do
    include_context "Do_Reqest","get",:new
  end

  describe "#show" do
    @blog = Factory.build(:blogc)
    include_context "Do_Reqest","get",:show,id: @blog.to_param
  end

  describe "#edit" do
    @blog = Factory.build(:blogc)
    include_context "Do_Reqest","get",:edit,id: @blog.to_param
  end

  describe "#create_true" do
    @blog = Factory.build(:blogc)
    @req = "post"
    @action = :create
    @params = {blog: @blog.attributes}
    @expects = "redirect"
    @objs = nil
    @result = "true"
    @template = nil
    @rto_path = nil
 
    include_context "Do_Reqest",@req,@action,@params,@expects,@objs,@result,@template,@rto_path
  end

  describe "#create_false" do
    @req = "post"
    @action = :create
    @params = {:blog => nil}
    @expects = "render"
    @objs = nil
    @result = "false"
    @template = "blogs/new"
    @rto_path = nil

    include_context "Do_Reqest",@req,@action,@params,@expects,@objs,@result,@template,@rto_path
  end

  describe "#update_true" do
    @blog = Factory.build(:blogc)
    @req = "put"
    @action = :update
    @params = {id: @blog.to_param, blog: @blog.attributes}
    @expects = "redirect"
    @objs = nil
    @result = "true"
    @template = nil
    @rto_path = nil
 
    include_context "Do_Reqest",@req,@action,@params,@expects,@objs,@result,@template,@rto_path
  end

  describe "#update_false" do
    @blog = Factory.build(:blogc)
    @req = "put"
    @action = :update
    @params = {id: @blog.to_param, blog: {id: nil ,title: nil}}
    @expects = "render"
    @objs = nil
    @result = "false"
    @template = "blogs/edit"
    @rto_path = nil

    include_context "Do_Reqest",@req,@action,@params,@expects,@objs,@result,@template,@rto_path
  end

  describe "#destroy_true" do
    @blog = Factory.build(:blogc)
    @req = "delete"
    @action = :destroy
    @params = {id: @blog.to_param}
    @expects = "redirect"
    @objs = nil
    @result = "true"
    @template = nil
    @rto_path = "blogs_path"

    include_context "Do_Reqest",@req,@action,@params,@expects,@objs,@result,@template,@rto_path
  end


end


BlogsControllerのクリエイトとアップデートアクションです

 # POST /blogs
  # POST /blogs.json


  def create
    @blog = Blog.create(current_user,params[:blog])
    @result = @blog.save

    respond_to do |format|
      if @result
        format.html { redirect_to @blog, notice: 'Blog was successfully updated' }
        format.json { render json: @blog, status: :created, location: @blog }
      else
        format.html { render action: "new" }
        format.json { render json: @blog.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /blogs/1
  # PUT /blogs/1.json
  def update
    @blog = Blog.find(params[:id])
    @result = @blog.update_attributes(params[:blog])

    respond_to do |format|
      if @result
        format.html { redirect_to @blog, notice: 'Blog was successfully updated.' }
        format.json { head :ok }
      else
        format.html { render action: "edit" }
        format.json { render json: @blog.errors, status: :unprocessable_entity }
      end
    end