ツイッター風Railsアプリをテストする（統合テスト編）

# この記事の基本的な方針
今回は別記事、[ツイッター風Railsアプリ最短復習（忙しい人の流し読みで開発シリーズ）](https://qiita.com/annaPanda8170/items/844684cb739f99d3f114)で作ったアプリの統合テストをします。

手を動かしながら読みたいようでしたら、以下でこのツイッター風Railsアプリを手に入れてください。

```bash:Terminal
$ git clone https://github.com/annaPanda8170/cheaptweet.git
$ bundle install
$ bundle exec rake db:create
$ bundle exec rake db:migrate
```
基本解説はしません。手順のみ示します。
# 想定する読み手
既に一度Railsアプリをチュートリアルやスクール等で作ったことがある方を想定しております。
Mac使用で、パソコンの環境構築は完了していることが前提です。
# 具体的な手順
[完成品GitHub](https://github.com/annaPanda8170/cheaptweet/tree/%E3%83%84%E3%82%A4%E3%83%83%E3%82%BF%E3%83%BC%E9%A2%A8Rails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E3%83%86%E3%82%B9%E3%83%88%E3%81%99%E3%82%8B%EF%BC%88%E7%B5%B1%E5%90%88%E3%83%86%E3%82%B9%E3%83%88%E7%B7%A8%EF%BC%89)
## ①準備
```ruby:Gemfile
#省略
group :development, :test do
#省略
  gem 'rspec-rails'
  gem 'factory_bot_rails'
end

group :development do
#省略
  gem 'spring-commands-rspec'
end
#省略
```
※spring-commands-rspecは起動時間を速くするためのものでなくても問題はありません。

```terminal:Terminal
$ bundle install
$ rails g rspec:install
$ rails g rspec:feature cheapTweet
$ rails g factory_bot:model user
$ rails g factory_bot:model tweet
$ bundle exec spring binstub rspec

control + c
$ rails s
```

```:.rspec
--format documentation
```
※これでRspecの出力が読みやすくなるそうです。


```ruby:spec/rails_helper.rb
#省略
require 'capybara/rspec'
include Warden::Test::Helpers
```


ここで空っぽのまま一度起動してみます。

```terminal:Terminal
$ bundle exec rspec
```

```terminal:Output
CheapTweets
  add some scenarios (or delete) /Users/handaryouhei/Desktop/cheaptweet/spec/features/cheap_tweet_spec.rb (PENDING: Not yet implemented)

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) CheapTweets add some scenarios (or delete) /Users/handaryouhei/Desktop/cheaptweet/spec/features/cheap_tweet_spec.rb
     # Not yet implemented
     # ./spec/features/cheap_tweet_spec.rb:4


Finished in 0.0014 seconds (files took 2.21 seconds to load)
1 example, 0 failures, 1 pending
```

```ruby:spec/factories/tweets.rb
FactoryBot.define do
  factory :tweet do
    text {"hello"}
    association :user
  end
end
```

```ruby:spec/factories/users.rb
FactoryBot.define do
  factory :user do
    sequence(:nickname) { |n| "annaPanda#{n}" }
    sequence(:email) { |n| "a#{n}@a" }
    password {"111111"}
    password_confirmation {"111111"}
  end
end
```

## ②テスト構築
```ruby:spec/features/cheap_tweet_spec.rb
require 'rails_helper'

RSpec.feature "CheapTweets", type: :feature do
  scenario "新規登録するとログアウトが表示されているTOP画面に遷移する" do
    visit root_path
    click_link "会員登録"
    fill_in "user_nickname", with: "annaPanda"
    fill_in "user_email", with: "a@a"
    fill_in "user_password", with: "111111"
    fill_in "user_password_confirmation", with: "111111"
    click_button "Sign up"
    expect(page).to have_content 'ログアウト'
  end
  scenario "ログインするとログアウトが表示されているTOP画面に遷移する" do
    user = FactoryBot.create(:user)
    visit root_path
    click_link "ログイン"
    fill_in "user_email", with: "a1@a"
    fill_in "user_password", with: "111111"
    click_button "Log in"
    expect(page).to have_content 'ログアウト'
  end
  scenario "非ログイン状態でTOP画面に遷移するとログアウトが表示されていない" do
    visit root_path
    expect(page).not_to have_content 'ログアウト'
  end
  scenario "投稿したらデータベースにtweetが一つ増える" do
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    expect{
      click_link "投稿"
      fill_in "tweet_text", with: "こんにちは"
      click_button "投稿"
    }.to  change( Tweet.all, :count ).by(1)
  end
  scenario "自分で投稿したtweetをshowして編集するとindexに反映される" do
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "投稿"
    fill_in "tweet_text", with: "こんにちは"
    click_button "投稿"
    click_link "こんにちは"
    click_link '編集'
    fill_in "tweet_text", with: "こんにちは！"
    click_button '編集'
    visit root_path
    expect(page).to have_content 'こんにちは！'
  end
  scenario "自分以外が投稿したtweetをshowすれば編集ボタンが表示されない" do
    tweet = FactoryBot.create(:tweet)
    visit root_path
    click_link "a"
    expect(page).not_to have_content '編集'
  end
  scenario "自分で投稿したtweetをshowして削除ボタンを押せばデータベースのtweetが一つ減る" do
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "投稿"
    fill_in "tweet_text", with: "こんばんは"
    click_button "投稿"
    click_link "こんばんは"
    expect{
      click_link "削除"
    }.to  change( Tweet.all, :count ).by(-1)
  end
  scenario "自分以外が投稿したtweetをshowすれば削除ボタンが表示されない" do
    tweet = FactoryBot.create(:tweet)
    visit root_path
    click_link "hello"
    expect(page).not_to have_content '削除'
  end
  scenario "ヘッダーの自分の名前をクリックすると自分の投稿一覧が表示される" do
    tweet = FactoryBot.create(:tweet)
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "投稿"
    fill_in "tweet_text", with: "おはよう"
    click_button "投稿"
    click_link "投稿"
    fill_in "tweet_text", with: "さようなら"
    click_button "投稿"
    within 'header' do
      click_link user.nickname
    end
    expect(page).to have_content 'おはよう'
    expect(page).to have_content 'さようなら'
    expect(page).not_to have_content 'hello'
  end
  scenario "TOP画面の投稿者をクリックするとそのユーザーの一覧が表示される" do
    tweet = FactoryBot.create(:tweet)
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "投稿"
    fill_in "tweet_text", with: "おはよう"
    click_button "投稿"
    click_link "投稿"
    fill_in "tweet_text", with: "さようなら"
    click_button "投稿"
    click_link tweet.user.nickname
    expect(page).not_to have_content 'おはよう'
    expect(page).not_to have_content 'さようなら'
    expect(page).to have_content 'hello'
  end
  scenario "コメントすると表示される" do
    tweet = FactoryBot.create(:tweet)
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "hello"
    fill_in "comment_text", with: "ハロー"
    click_button "コメント"
    expect(page).to have_content 'ハロー'
  end
end
```

`click_link`と`click_button`で指定しているのはテキストです。
`fill_in`で指定しているのはidです。
`have_content`で表示されているかを確認しています。

```terminal:Terminal
$ bundle exec rspec
```

```terminal:Output
CheapTweets
  新規登録するとログアウトが表示されているTOP画面に遷移する
  ログインするとログアウトが表示されているTOP画面に遷移する
  非ログイン状態でTOP画面に遷移するとログアウトが表示されていない
  投稿したらデータベースにtweetが一つ増える
  自分で投稿したtweetをshowして編集するとindexに反映される
  自分以外が投稿したtweetをshowすれば編集ボタンが表示されない
  自分で投稿したtweetをshowして削除ボタンを押せばデータベースのtweetが一つ減る
  自分以外が投稿したtweetをshowすれば削除ボタンが表示されない
  ヘッダーの自分の名前をクリックすると自分の投稿一覧が表示される
  TOP画面の投稿者をクリックするとそのユーザーの一覧が表示される
  コメントすると表示される

Finished in 0.87206 seconds (files took 2.18 seconds to load)
11 examples, 0 failures
```
# まとめ
読むだけなら割と直感的にいけると思います。
この統合テストさえ済ませていれば、コントローラは不要なのでは？という考えもあるようです。
モデルのテストとこの統合テストを行うことがRailsアプリのテストのスタンダードになるかもしれませんね。