Chapter 25

4-6. Home#top の削除

Masuyama
Masuyama
2022.10.15に更新

Home#top の削除

さて、現在は '/' にアクセスすると HomeController の #top アクションが呼ばれ、仮のトップページが表示されていました。

スクリーンショット 2022-08-08 9 15 57

しかし TechLog ではトップページに学習ログ一覧を表示したいため、HomeController 関連をすべて消していきます。
ただし、ナビゲーションバーのテストは HomeController#top で検証していたため、そのあたりを投稿機能のテストに移行することに注意してください。

今回は HomeController に関し、以下の変更を行います。

  • テストの変更
    • Request Spec
      • posts のルーティング
      • home の Request Spec 削除
    • System Spec
      • ナビゲーションバーのテストを posts へ移行
      • posts#index のルーティング
      • home の System Spec 削除
  • ルーティングの変更
  • HomeController の削除
  • View の削除

テストの変更

今回は大きな変更であり、かつやることが明確であるためテストから先に変更してみましょう。

Request Spec

まずは Request Spec の変更を行います。

posts#index のルーティング

現在、posts#index へのパスは /posts でした。
それがこれからはトップページのパス、つまり / となるのでテストを変更しておきます。

変更前: spec/requests/posts_spec.rb

  describe 'GET /posts' do
    context 'ログインしていない場合' do
      it 'HTTPステータス200を返す' do
        get '/posts'
        expect(response).to have_http_status '200'
      end
    end
    context 'ログインしている場合' do
      it 'HTTPステータス200を返す' do
        sign_in @user
        get '/posts'
        expect(response).to have_http_status '200'
      end
    end
  end

変更後: spec/requests/posts_spec.rb

  describe 'GET /' do # 修正
    context 'ログインしていない場合' do
      it 'HTTPステータス200を返す' do
        get '/' # 修正
        expect(response).to have_http_status '200'
      end
    end
    context 'ログインしている場合' do
      it 'HTTPステータス200を返す' do
        sign_in @user
        get '/' # 修正
        expect(response).to have_http_status '200'
      end
    end
  end

System Spec

次は System Spec に変更を行います。

ナビゲーションバーのテスト移行

home の System Spec には、ナビゲーションバーに関する以下のテストが含まれていました。

<details>
<summary>spec/system/home_spec.rb</summary>

...
  describe 'ナビゲーションバーの検証' do
    context 'ログインしていない場合' do
      before { visit '/' }

      it 'ログ一覧リンクを表示する' do
        expect(page).to have_link('ログ一覧', href: '/posts')
      end

      it 'ユーザー登録リンクを表示する' do
        expect(page).to have_link('ユーザー登録', href: '/users/sign_up')
      end

      it 'ログインリンクを表示する' do
        expect(page).to have_link('ログイン', href: '/users/sign_in')
      end

      it 'ログ投稿リンクを表示しない' do
        expect(page).not_to have_link('ログ投稿', href: '/posts/new')
      end

      it 'ログアウトリンクは表示しない' do
        expect(page).not_to have_content('ログアウト')
      end
    end

    context 'ログインしている場合' do
      before do
        user = create(:user) # ログイン用のユーザーを作成
        sign_in user # 作成したユーザーでログイン
        visit '/'
      end

      it 'ログ一覧リンクを表示する' do
        expect(page).to have_link('ログ一覧', href: '/posts')
      end

      it 'ユーザー登録リンクは表示しない' do
        expect(page).not_to have_link('ユーザー登録', href: '/users/sign_up')
      end

      it 'ログインリンクは表示しない' do
        expect(page).not_to have_link('ログイン', href: '/users/sign_in')
      end

      it 'ログ投稿リンクを表示する' do
        expect(page).to have_link('ログ投稿', href: '/posts/new')
      end

      it 'ログアウトリンクを表示する' do
        expect(page).to have_content('ログアウト')
      end

      it 'ログアウトリンクが機能する' do
        click_button 'ログアウト'

        # ログインしていない状態のリンク表示パターンになることを確認
        expect(page).to have_link('ユーザー登録', href: '/users/sign_up')
        expect(page).to have_link('ログイン', href: '/users/sign_in')
        expect(page).not_to have_button('ログアウト')
      end
    end
  end
end

</details>

ログイン状態に応じたナビゲーションバーの表示のテストを home の System Spec にまとめて書いていましたが、
中身は「ユーザー機能」と「ログ投稿機能」に関するテストが混在していました。

この機会に、それぞれ users の System Spec と posts の System Spec に移行しましょう。
home の System Spec をそれぞれの System Spec にコピペで貼り付け、それぞれの機能に関する箇所だけを残すようにすると移行が楽です。

spec/system/posts_spec.rb

...
  #### ここから追加 ####
  describe 'ナビゲーションバーの検証' do
    context 'ログインしていない場合' do
      before { visit '/' }

      it 'ログ一覧リンクを表示する' do
        expect(page).to have_link('ログ一覧', href: '/posts')
      end

      it 'ログ投稿リンクを表示しない' do
        expect(page).not_to have_link('ログ投稿', href: '/posts/new')
      end
    end

    context 'ログインしている場合' do
      before do
        user = create(:user) # ログイン用のユーザーを作成
        sign_in user # 作成したユーザーでログイン
        visit '/'
      end

      it 'ログ一覧リンクを表示する' do
        expect(page).to have_link('ログ一覧', href: '/posts')
      end

      it 'ログ投稿リンクを表示する' do
        expect(page).to have_link('ログ投稿', href: '/posts/new')
      end
    end
  end
  #### ここまで追加 ####

spec/system/users_spec.rb

...
  #### ここから追加 ####
  describe 'ナビゲーションバーの検証' do
    context 'ログインしていない場合' do
      before { visit '/' }

      it 'ユーザー登録リンクを表示する' do
        expect(page).to have_link('ユーザー登録', href: '/users/sign_up')
      end

      it 'ログインリンクを表示する' do
        expect(page).to have_link('ログイン', href: '/users/sign_in')
      end

      it 'ログアウトリンクは表示しない' do
        expect(page).not_to have_content('ログアウト')
      end
    end

    context 'ログインしている場合' do
      before do
        user = create(:user) # ログイン用のユーザーを作成
        sign_in user # 作成したユーザーでログイン
        visit '/'
      end

      it 'ユーザー登録リンクは表示しない' do
        expect(page).not_to have_link('ユーザー登録', href: '/users/sign_up')
      end

      it 'ログインリンクは表示しない' do
        expect(page).not_to have_link('ログイン', href: '/users/sign_in')
      end

      it 'ログアウトリンクを表示する' do
        expect(page).to have_content('ログアウト')
      end

      it 'ログアウトリンクが機能する' do
        click_button 'ログアウト'

        # ログインしていない状態のリンク表示パターンになることを確認
        expect(page).to have_link('ユーザー登録', href: '/users/sign_up')
        expect(page).to have_link('ログイン', href: '/users/sign_in')
        expect(page).not_to have_button('ログアウト')
      end
    end
  end
  #### ここまで追加 ####

posts#index のルーティング

Post の System Spec についてですが、投稿一覧のパスは /posts ではなく / になったので修正しておきましょう。

spec/system/posts_spec.rb

require 'rails_helper'

describe 'Post', type: :system do
...
  describe 'ログ投稿機能の検証' do
    ...
    context 'ログインしている場合' do
      ...
      context 'パラメータが正常な場合' do
        it 'Postを作成できる' do
          expect { subject }.to change(Post, :count).by(1)
          expect(current_path).to eq('/') # 修正
          ...
  describe 'ログ一覧機能の検証' do
    before { visit '/' } # 修正
    ...
  describe 'ログ削除機能の検証' do
    context '投稿したユーザーでログインしている場合' do
      ...
      it '削除ボタンをクリックすると削除できる' do
        ...
        # リダイレクト後の画面確認
        expect(current_path).to eq('/') # 修正
        ...
  describe 'ナビゲーションバーの検証' do
    context 'ログインしていない場合' do
      before { visit '/' }

      it 'ログ一覧リンクを表示する' do
        expect(page).to have_link('ログ一覧', href: '/') # 修正
      end
      ...
    context 'ログインしている場合' do
      ...
      it 'ログ一覧リンクを表示する' do
        expect(page).to have_link('ログ一覧', href: '/posts') # 修正
        ...

修正後の Post System Spec の全文は以下のようになります。

<details>
<summary>spec/system/posts_spec.rb</summary>

require 'rails_helper'

describe 'Post', type: :system do
  before do
    driven_by :selenium_chrome_headless # ヘッドレスモードで実行
    @user = create(:user) # ログイン用ユーザー作成
    @post = create(:post, title: 'RSpec学習完了', content: 'System Specを作成した', user_id: @user.id)
    @post2 = create(:post, title: 'RSpec学習完了 2', content: 'System Specを作成した 2', user_id: @user.id)
  end

  # 投稿フォーム
  let(:title) { 'テストタイトル' }
  let(:content) { 'テスト本文' }

  describe 'ログ投稿機能の検証' do
    # ログ投稿を行う一連の操作を subject にまとめる
    subject do
      fill_in 'post_title', with: title
      fill_in 'post_content', with: content
      click_button 'ログを記録'
    end

    context 'ログインしていない場合' do
      before { visit '/posts/new' }
      it 'ログインページへリダイレクトする' do
        expect(current_path).to eq('/users/sign_in')
        expect(page).to have_content('ログインしてください。')
      end
    end

    context 'ログインしている場合' do
      before do
        sign_in @user
        visit '/posts/new'
      end
      it 'ログインページへリダイレクトしない' do
        expect(current_path).not_to eq('/users/sign_in')
      end

      context 'パラメータが正常な場合' do
        it 'Postを作成できる' do
          expect { subject }.to change(Post, :count).by(1)
          expect(current_path).to eq('/')
          expect(page).to have_content('投稿しました')
        end
      end

      context 'パラメータが異常な場合' do
        let(:title) { nil }
        it 'Postを作成できない' do
          expect { subject }.not_to change(Post, :count)
          expect(page).to have_content('投稿に失敗しました')
        end
        it '入力していた内容は維持される' do
          subject
          expect(page).to have_field('post_content', with: content)
        end
      end
    end
  end

  describe 'ログ詳細機能の検証' do
    before { visit "/posts/#{@post.id}" }

    it 'Postの詳細が表示される' do
      expect(page).to have_content('RSpec学習完了')
      expect(page).to have_content('System Specを作成した')
      expect(page).to have_content(@user.nickname)
    end
  end

  describe 'ログ一覧機能の検証' do
    before { visit '/posts' }

    it '1件目のPostの詳細が表示される' do
      expect(page).to have_content('RSpec学習完了')
      expect(page).to have_content('System Specを作成した')
      expect(page).to have_content(@user.nickname)
    end

    it '2件目のPostの詳細が表示される' do
      expect(page).to have_content('RSpec学習完了 2')
      expect(page).to have_content('System Specを作成した 2')
      expect(page).to have_content(@user.nickname)
    end

    it '投稿タイトルをクリックすると詳細ページへ遷移する' do
      click_link 'RSpec学習完了'
      expect(current_path).to eq("/posts/#{@post.id}")
    end
  end

  describe 'ログ削除機能の検証' do
    context '投稿したユーザーでログインしている場合' do
      before do
        sign_in @user
        visit "/posts/#{@post.id}"
      end

      it '削除ボタンを表示する' do
        expect(page).to have_button('削除')
      end

      it '削除ボタンをクリックすると削除できる' do
        expect do
          click_button '削除'
        end.to change(Post, :count).by(-1) # 削除ボタンをクリックするとPostが1つ減る

        # リダイレクト後の画面確認
        expect(current_path).to eq('/')
        expect(page).to have_content('投稿が削除されました') # フラッシュメッセージを表示
        expect(page).not_to have_link("/posts/#{@post.id}") # 削除した投稿(の詳細ページへのリンク)が存在しない
      end
    end

    context '投稿したユーザーでログインしていない場合' do
      it '削除ボタンを表示しない' do
        visit "/posts/#{@post.id}"
        expect(page).not_to have_button('削除')
      end

      it '直接リクエストを投げても削除されない' do
        visit "/posts/#{@post.id}"

        expect do
          delete post_path(@post) # 投稿データを削除するリクエストを送る
        end.not_to change(Post, :count)
      end
    end
  end

  describe 'ナビゲーションバーの検証' do
    context 'ログインしていない場合' do
      before { visit '/' }

      it 'ログ一覧リンクを表示する' do
        expect(page).to have_link('ログ一覧', href: '/')
      end

      it 'ログ投稿リンクを表示しない' do
        expect(page).not_to have_link('ログ投稿', href: '/posts/new')
      end
    end

    context 'ログインしている場合' do
      before do
        user = create(:user) # ログイン用のユーザーを作成
        sign_in user # 作成したユーザーでログイン
        visit '/'
      end

      it 'ログ一覧リンクを表示する' do
        expect(page).to have_link('ログ一覧', href: '/')
      end

      it 'ログ投稿リンクを表示する' do
        expect(page).to have_link('ログ投稿', href: '/posts/new')
      end
    end
  end
end

</details>

ルーティングの変更

ではここから、テストが通るように変更を加えていきましょう。
まずはルーティングの設定からです。

root ('/') にアクセスした時に学習ログ一覧を表示させるため、ルーティング設定を変更します。
そのために、root 指定で呼ぶアクションを posts#index に変更します。

config/routes.rb

Rails.application.routes.draw do
  devise_for :users
  root 'posts#index' # 修正
  
  resources :posts, only: [:new, :create, :show, :destroy] # 修正
end

また、resources 内の :index 指定は不要になるので削除していることに注意してください。。

PostsController の変更

PostsController の create アクションと destroy アクションんでは、処理の成功時に投稿一覧へリダイレクトするようにしていました。
そのリダイレクト先の URL は posts_path と指定していましたので、ここを root_path に変更しておきます。

app/controllers/posts_controller.rb

  def create
    @post = Post.new(post_params)
    @post.user_id = current_user.id # ログインユーザのIDを代入
    if @post.save
      flash[:notice] = '投稿しました'
      redirect_to root_path # 修正
    else
      flash[:alert] = '投稿に失敗しました'
      render :new
    end
  end

  ...
  def destroy
    @post = Post.find_by(id: params[:id])
    if @post.user == current_user
      @post.destroy
      flash[:notice] = '投稿が削除されました'
    end
    redirect_to root_path # 修正
  end

ナビゲーションバー内リンクの変更

最後に、ナビゲーションバーの「ログ一覧」リンクのパスも変更します。
ここも posts_path で指定していたので root_path に置き換えてあげましょう。

(修正前): source/techlog/saigen/app/views/shared/_navbar.html.erb

...
<%= link_to "ログ一覧", posts_path, class: "block py-2 pr-4 pl-3 text-gray-400 hover:text-white border-b border-gray-700 hover:bg-gray-700 md:hover:bg-transparent md:border-0 md:hover:text-blue-white md:p-0" %>

(修正後): source/techlog/saigen/app/views/shared/_navbar.html.erb

...
<%= link_to "ログ一覧", root_path, class: "block py-2 pr-4 pl-3 text-gray-400 hover:text-white border-b border-gray-700 hover:bg-gray-700 md:hover:bg-transparent md:border-0 md:hover:text-blue-white md:p-0" %>

Home 関連のファイル削除

Home に関するファイルは不要になるので、すべて削除します。

まずはControllerからです。

$ rm app/controllers/home_controller.rb

エディタの操作で削除しても OK です。
ファイルが削除されていることを確認してください。

次はView です。
ディレクトリごと削除します。

$ rm -rf app/views/home

最後にテストです。
Request Specも、System Spec も不要になった削除します。

$ rm spec/requests/home_spec.rb
$ rm spec/system/home_spec.rb

テスト実行

変更もすべて完了したので、すべてのテストに通ることを確認します。

$ bin/rspec
Finished in 12.2 seconds (files took 0.28092 seconds to load)
60 examples, 0 failures

変更をコミット

では今回の変更をコミットしておきましょう。

$ git add .
$ git commit -m "Home関連機能を削除"
$ git push

今回は変更点が多かったので大変かもしれませんが、テストが失敗することで修正漏れに気付けるようになっているので安心してください!