[Rails] Rspec ポートフォリオに実装したrequest spec

# はじめに
ポートフォリオに実際に実装したrequest specの部分です。
テストコードについては勉強したけどテストコードを実際に書く時にどんなことをテストすればいいか分からないと思ったのでそういった人の参考になれば嬉しいです。
テストしたのはCRUD機能部分です。

## index

### Controller

`with_attached_eyecatch`はactive storageでN+1が起きないするためのメソッドです。

```ruby:app/controllers/event_controller.rb
def index
  @events = Event.with_attached_eyecatch.latest
end
```

### request spec
`subject`を使うことでリクエストを共通化しています。

```ruby:spec/requests/events_spec.rb
describe 'GET #index' do
  subject { get(events_path) }
  context 'イベントが存在する場合' do
    let!(:event) { create(:event, user_id: user.id) }

    it 'リクエストが成功する' do
      subject
      expect(response).to have_http_status(200)
    end

    it 'name が表示されている' do
      subject
      expect(response.body).to include event.name
    end
  end
end
```

## show

### Controller

```ruby:app/controllers/event_controller.rb
def show
  @event = Event.find(params[:id])
end
```

### request spec

```ruby:spec/requests/events_spec.rb
describe 'GET #show' do
  subject { get(event_path(event.id)) }

  context 'イベントが存在する場合' do
    let(:event) { create(:event, user_id: user.id) }

    it 'リクエストが成功する' do
      subject
      expect(response).to have_http_status(200)
    end

    it 'name が表示されている' do
      subject
      expect(response.body).to include event.name
    end
  end
end
```

## new

### Controller

```ruby:app/controllers/event_controller.rb
def new
  @event = current_user.events.build
end
```
### request spec

```ruby:spec/requests/events_spec.rb
describe 'GET #new' do
  subject { get(new_event_path) }

  it 'リクエストが成功する' do
    subject
    expect(response).to have_http_status(200)
  end
end
```

## post

### Controller
```ruby:app/controllers/event_controller.rb
def create
  @event = current_user.events.build(event_params)
  if @event.save
    redirect_to event_path(@event), notice: '作成しました'
  else
    flash.now[:error] = '作成に失敗しました'
    render :new
  end
end
```

### request spec
パラメーターが必要なのでパラメーターが正常のテストと異常のテストの両方をテストします。
異常系のテストはバリデーションエラーが出ることをテストしています。

```ruby:spec/requests/events_spec.rb
describe 'POST #create' do
  subject { post(events_path, params: params) }

  context 'パラメータが正常な場合' do
    let(:params) { { event: attributes_for(:event) } }

    it '投稿が保存される' do
      expect { subject }.to change { Event.count }.by(1)
    end

    it 'リクエストが成功する' do
      subject
      expect(response).to have_http_status(302)
    end

    it 'イベント一覧ページにリダイレクトされる' do
      subject
      expect(response).to redirect_to('http://www.example.com/events?locale=ja')
    end
  end

  context 'パラメータが異常な場合' do
    let(:params) { { event: attributes_for(:event, :invalid) } }

    it 'リクエストが成功する' do
      subject
      expect(response).to have_http_status(200)
    end

    it '投稿が保存されない' do
      expect { subject }.not_to change(Event, :count)
    end

    it 'イベント投稿ページがレンダリングされる' do
      subject
      expect(response.body).to include I18n.t('events.form.event-post')
    end
  end
end
```

## edit

### Controller
```ruby:app/controllers/event_controller.rb
def edit
  @event = current_user.events.find(params[:id])
end
```

### request spec
```ruby:spec/requests/events_spec.rb
describe 'GET #edit' do
  subject { get(edit_event_path(event_id)) }

  context 'イベントが存在する場合' do
    let(:event) { create(:event, user_id: user.id) }
    let(:event_id) { event.id }

    it 'リクエストが成功する' do
      subject
      expect(response).to have_http_status(200)
    end

    it 'name が表示されている' do
      subject
      expect(response.body).to include event.name
    end
  end
end
```

## update
### Controller
```ruby:app/controllers/event_controller.rb
def update
  @event = current_user.events.find(params[:id])
  if @event.update(event_params)
    redirect_to event_path(@event), notice: '更新しました'
  else
    flash.now[:error] = '更新に失敗しました'
    render :edit
  end
end
```

### request spec
パラメーターが必要なので異常系のテストも必要。
createとの違いはデータの更新ができていること`reload`メソッドを使うことで更新した値のテストが可能。

```ruby:spec/requests/events_spec.rb
describe 'PATCH #update' do
  subject { patch(event_path(event.id), params: params) }
  let(:event) { create(:event, user_id: user.id) }

  context 'パラメーターが正常な場合' do
    let(:params) { { event: attributes_for(:event) } }

    it 'リクエストが成功する' do
      subject
      expect(response).to have_http_status(302)
    end

    it 'name が更新される' do
      origin_name = event.name
      new_name = params[:event][:name]
      expect { subject }.to change { event.reload.name }.from(origin_name).to(new_name)
    end
  end

  context 'パラメータが異常なとき' do
    let(:params) { { event: attributes_for(:event, :invalid) } }

    it 'リクエストが成功する' do
      subject
      expect(response).to have_http_status(200)
    end

    it 'name が更新されない' do
      expect { subject }.not_to change(event.reload, :name)
    end

    it 'イベント編集ページがレンダリングされる' do
      subject
      expect(response.body).to include I18n.t('events.form.event-edit')
    end
  end
end
```

## destroy

### Controller
```ruby:app/controllers/event_controller.rb
def destroy
  event = current_user.events.find(params[:id])
  event.destroy!
  redirect_to events_path, notice: '削除しました'
end
```

### request spec
`let!`とすることで各テストの最初に実行される。

```ruby:spec/requests/events_spec.rb
describe 'DELETE #destroy' do
  subject { delete(event_path(event.id)) }
  let!(:event) { create(:event, user_id: user.id) }

  context 'パラメータが正常な場合' do
    it 'リクエストが成功する' do
      subject
      expect(response).to have_http_status(302)
    end

    it 'イベントが削除される' do
      expect { subject }.to change(Event, :count).by(-1)
    end

    it 'イベント一覧にリダイレクトすること' do
      subject
      expect(response).to redirect_to('http://www.example.com/events?locale=ja')
    end
  end
end
```

以上です。
最後はsystem spec!