shared_exampleを使ってrequire_login関係のrspecの重複を減らす

テストの冗長を減らせる良い書き方だと思ったので、自分への備忘録も兼ねて、情報を共有します。

#実現方法
`shared_example`を使って下記の処理をまとめました。

- `require_login`に引っかかった時の確認項目
- `require_login`に引っかからないための`current_user`の定義

##冗長な部分をまとめた例

````spec/controllers/books_controller_spec.rb
RSpec.describe BooksController, type: :controller do
  describe 'GET #new' do

    it_behaves_like 'before login', :get :new

    it_behaves_like 'after login as a user' do
      it 'render_template :new' do
        get :new
        expect(response).to render_template(:new)
      end
    end
  end

  describe 'POST #create' do

    it_behaves_like 'before login', :post, :create, { book: { name: 'メタプログラミングRuby' } }

    it_behaves_like 'after login as a user' do

      it 'if succeeded posting, render template :show' do
        post :create, { book: { name: 'メタプログラミングRuby' } }
        expect(response).to render_template(:show)
      end

      it 'if failed posting, render template :new' do
        post :create, { book: { name: '' } }
        expect(response).to render_template(:new)
      end
    end
  end
end
````

````spec/support/shared_examples.rb
# require_loginに引っかかった時の確認項目
shared_examples 'before login' do |method, template, params|
  before do
    send(method, template, params)
  end
  it 'redirect to login' do
    expect(response).to redirect_to(login_path)
  end
end

# require_loginに引っかからないためのcurrent_userの定義
shared_examples 'after login as a user' do
  before do
    user = User.first
    if user.blank?
      user = User.create(email: 'test@example.com', password: 'testpassword', password_confirmation: 'testpassword')
      user.activate!
    end
    allow(controller)
      .to receive(:current_user)
      .and_return(user)
  end
end
````

##冗長な例

重複をまとめずに書いていたときは、自分の場合こうなっていました。

````spec/controllers/books_controller_spec.rb
RSpec.describe BooksController, type: :controller do

  describe 'GET #new' do
    # require_loginに引っかかった場合の処理を毎回書きたくない
    it 'before login' do
      get :new
      expect(response).to redirect_to(login_path)
    end

    describe 'after login as a user' do
      # current_userの定義を毎回に書きたくない
      before do
        user = User.first
        if user.blank?
          user = User.create(email: 'test@example.com', password: 'password', password_confirmation: 'password')
          user.activate!
        end
        allow(controller)
          .to receive(:current_user)
          .and_return(user)
      end

      it 'render_template :new' do
        get :new
        expect(response).to render_template(:new)
      end
    end
  end

  describe 'POST #create' do

    # newの場合とほとんど同じ
    it 'before login' do
      post :create, { book: { name: 'メタプログラミングRuby' } }
      expect(response).to redirect_to(login_path)
    end

    it 'after login as a user' do

      # newの場合とほとんど同じ
      before do
        user = User.first
        if user.blank?
          user = User.create(
            email: 'test@example.com',
            password: 'password',
            password_confirmation: 'password'
          )
          user.activate!
        end
        allow(controller)
          .to receive(:current_user)
          .and_return(user)
      end

      it 'if succeeded posting, render template :show' do
        post :create, { book: { name: 'メタプログラミングRuby' } }
        expect(response).to render_template(:show)
      end

      it 'if failed posting, render template :new' do
        post :create, { book: { name: '' } }
        expect(response).to render_template(:new)
      end
    end
  end
end
````

以上です。
誰かのお役に立てれば幸いです。

参考：
http://d.hatena.ne.jp/yohfee/20110222/1298384637
http://qiita.com/asukiaaa/items/2dd7ed44f6903bc15055