質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

2回答

1500閲覧

rspecにてundefined method `already_liked?' for nil:NilClass

Engineer_traine

総合スコア17

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2021/10/13 08:04

編集2021/10/13 11:42

前提・実現したいこと

現在既存のアプリ(カフェ登録ができるようなアプリ)に対していいね機能を実装したのですが
ローカル環境ではいいね機能もしっかり機能していたのでrspecでテストを行ったところ
タイトルのエラーが発生しました。
恐らくcurrent_userがnilだとはいうことだと思いますがイマイチ現状なぜエラーが発生しているのか
掴めていません。
ちなみにログイン機能を実装した際はgem 'devise'は使用していません。
そのためかcurrent_userが使用できないのかと考えてはいましたが他の箇所でもcurrent_userを
使用していました。
application_controller.rbではdef current_userで定義してあります。
またrspecにてbeforeでログインしていないという動作を怠ったということも考えましたが
ちゃんとログインするような記述はしておりました。
どなたか原因特定できる方もしいましたらご教授お願い致します。

発生している問題・エラーメッセージ

1) Caves #show カフェ詳細画面の表示に成功すること Failure/Error: <% if current_user.already_liked?(@cafe) %> ActionView::Template::Error: undefined method `already_liked?' for nil:NilClass # ./app/views/caves/show.html.erb:73:in `_app_views_caves_show_html_erb__3062244970416319179_74500' # ./spec/requests/caves_spec.rb:45:in `block (3 levels) in <main>' # ------------------ # --- Caused by: --- # NoMethodError: # undefined method `already_liked?' for nil:NilClass # ./app/views/caves/show.html.erb:73:in `_app_views_caves_show_html_erb__3062244970416319179_74500'

該当のソースコード

<% provide(:title, "cafe") %> <%= render "shared/login_header" %> <div class="detail_title">カフェ詳細</div> <div class="cafe_detail"> <p class="img_detail"> <% if @cafe.image? %> <%= image_tag @cafe.image.to_s %> <% else %> <p>画像なし</p> <% end %> </p> <div class="cafe_description"> <p class="cafe_item"> <strong> <i class="fas fa-store"></i>店名: </strong> <%= @cafe.name %> </p> <p class="cafe_item"> <strong> <i class="fas fa-map-marker"></i>住所: </strong> <%= @cafe.address %> </p> <%= render 'shared/cafe_map', cafe: @cafe %> <p class="cafe_item"> <strong> <i class="fas fa-clock"></i>営業時間: </strong> <%= @cafe.business_hours.strftime( "%H:%M" ) %>~<%= @cafe.end_business_hours.strftime( "%H:%M" ) %> </p> <p class="cafe_item"> <strong> <i class="fas fa-lightbulb"></i>コンセント席数: </strong> <%= @cafe.number_seats %>席 </p> <p class="cafe_item"> <strong> <i class="fas fa-wifi"></i>Wifi: </strong> <% if @cafe.wifi == true %> <%= @cafe.wifi="◯" %> <% else %> <%= @cafe.wifi="×" %> <% end %> </p> </div> <div class="detail_link"> <%= link_to '更新する', edit_cafe_path(@cafe) %> | <%= link_to '戻る', caves_path %> </div> </div> <div class="detail_comment">コメント一覧</div> <div class="comment_space"> <% @comments.each do |c| %> <div class="user_comment"> <a href="/users/<%= @cafe.user_id %>"><%= c.user.email %></a> <%= c.content %> <hr> </div> <% end %> <div class="comment_button"> <%= form_for [@cafe, @comment] do |f| %> <%= f.text_field :content %> <br> <%= f.submit 'コメントする' %> <% end %> </div> </div> <div class="like_space"> <div class="like_number">いいね件数: <%= @cafe.likes.count %></div> <% if current_user.already_liked?(@cafe) %> <%= button_to 'いいねを取り消す', cafe_like_path(@cafe), method: :delete %> <% else %> <%= button_to 'いいね', cafe_likes_path(@cafe) %> <% end %> </div>
class CavesController < ApplicationController before_action :set_cafe, only: [:show, :edit, :update, :destroy] # GET /caves or /caves.json def index @q = Cafe.ransack(params[:q]) @caves = @q.result(distinct: true).page(params[:page]).per(8) end # GET /caves/1 or /caves/1.json def show @cafe = Cafe.find(params[:id]) @comments = @cafe.comments @comment = Comment.new @like = Like.new end # GET /caves/new def new @cafe = Cafe.new end # GET /caves/1/edit def edit end # POST /caves or /caves.json def create @cafe = Cafe.new(cafe_params) @cafe.user_id = current_user.id respond_to do |format| if @cafe.save format.html { redirect_to @cafe, notice: "登録しました" } format.json { render :show, status: :created, location: @cafe } else format.html { render :new, status: :unprocessable_entity } format.json { render json: @cafe.errors, status: :unprocessable_entity } end end end # PATCH/PUT /caves/1 or /caves/1.json def update respond_to do |format| if @cafe.update(cafe_params) format.html { redirect_to @cafe, notice: "更新しました" } format.json { render :show, status: :ok, location: @cafe } else format.html { render :edit, status: :unprocessable_entity } format.json { render json: @cafe.errors, status: :unprocessable_entity } end end end # DELETE /caves/1 or /caves/1.json def destroy @cafe.destroy respond_to do |format| format.html { redirect_to caves_url, notice: "削除しました" } format.json { head :no_content } end end private def set_cafe @cafe = Cafe.find(params[:id]) end def cafe_params params.require(:cafe).permit(:image, :name, :address, :business_hours, :end_business_hours, :number_seats, :wifi, :user_id, :latitude, :longitude, :content) end end
class User < ApplicationRecord has_secure_password has_many :likes has_many :caves, dependent: :destroy has_many :like_caves, through: :likes, source: :cafe has_many :comments VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+.[a-z]+\z/i.freeze validates :email, { presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } } VALID_PASSWORD_REGEX = /\A[\w\-]+\z/.freeze validates :password, { presence: true, length: { minimum: 8 }, format: { with: VALID_PASSWORD_REGEX }, allow_nil: true } validates :password_confirmation, presence: true def already_liked?(cafe) likes.exists?(cafe_id: cafe.id) end end
require 'rails_helper' RSpec.describe "Caves", type: :request do describe "#new" do let(:cafe) { create(:cafe) } let(:user) { create(:user) } before do session_params = { session: { email: user.email, password: user.password } } post "/login", params: session_params end it 'カフェ登録画面の表示に成功すること' do get '/caves/new' expect(response).to have_http_status(200) end end describe "#create" do let(:cafe) { create(:cafe) } let(:user) { create(:user) } before do session_params = { session: { email: user.email, password: user.password } } post "/login", params: session_params end it 'カフェ作成に成功すること' do valid_params = { image: cafe.image, name: cafe.name, address: cafe.address, number_seats: cafe.number_seats } expect { post '/caves/new', params: { cafe: valid_params } } expect(response).to have_http_status(200) end end describe "#show" do let(:cafe) { create(:cafe) } let(:user) { create(:user) } before do session_params = { session: { email: user.email, password: user.password } } post "/login", params: session_params end it 'カフェ詳細画面の表示に成功すること' do get "/caves/#{cafe.id}" expect(response).to have_http_status(200) end end describe "#edit" do let(:cafe) { create(:cafe) } let(:user) { create(:user) } before do session_params = { session: { email: user.email, password: user.password } } post "/login", params: session_params end it 'カフェ編集画面の表示に成功すること' do get "/caves/#{cafe.id}/edit" expect(response).to have_http_status(200) end end describe "#update" do let(:cafe) { create(:cafe) } let(:user) { create(:user) } before do session_params = { session: { email: user.email, password: user.password } } post "/login", params: session_params end it 'カフェの編集に成功すること' do get "/caves/#{cafe.id}/edit" valid_params = { image: cafe.image, name: cafe.name, address: cafe.address, number_seats: cafe.number_seats } expect { put "/caves/#{cafe.id}", params: { cafe: valid_params } } expect(response).to have_http_status(200) end end end
class ApplicationController < ActionController::Base helper_method :current_user def login? if current_user.nil? redirect_to login_path, alert: "ログインが必要です。" end end def already_login? unless current_user.nil? redirect_to user_path, notice: "ログイン済みです。" end end def current_user if session[:user_id] current_user ||= User.find(session[:user_id]) end current_user end end

試したこと

controllerのshowの箇所に@user = current_user.idの記載をしてみましたが
idがnilにエラーメッセージは変更されました。
gemで'devise'を使用していないことが原因のようにも感じております。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

describe "#show" do let(:cafe) { create(:cafe) } let(:user) { create(:user) } before do session_params = { session: { email: user.email, password: user.password } } post "/login", params: session_params end it 'カフェ詳細画面の表示に成功すること' do get "/caves/#{cafe.id}" expect(response).to have_http_status(200) end end

before でログインしていますが、たぶんこれは get "/caves/#{cafe.id}" には影響しません。
以下のようにして session が user_id を返すようにします。

before do allow_any_instance_of(ActionDispatch::Request) .to receive(:session) { { user_id: user.id } } end

他のテストが通るのはたまたま current_user に依存していないからじゃないでしょうか。

投稿2021/10/13 18:01

neko_daisuki

総合スコア2090

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Engineer_traine

2021/10/14 12:11

返信方遅れてしまい申し訳ありません。 ご回答ありがとうございます。 ご教授いただいた内容をもとに自分の開発環境で試してみたところ正常通り機能しテストも 無事通すことができました。 仰っていました通り、いいね機能の実装以外でviewでcurrent_userをしている箇所は他には ないのでそのような結果を招いていたようです。 今回は本当にありがとうございました。
guest

0

session: {email: user.email, password: user.password }

ここの user.passwordが怪しい。
ここはこのuserを作ったときに設定した password の文字列を直接入れてください

投稿2021/10/13 10:45

winterboum

総合スコア23567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Engineer_traine

2021/10/13 11:39

ご回答ありがとうございます。 ご指摘頂いた点を修正してみましたがエラー内容変わりませんでした。 ちなみにshow以外のテストは全て通ります。 また一応下記factorybotで作成していたコードになります。 FactoryBot.define do factory :user do sequence(:email) { |n| "person#{n}@example.com" } password { 'password' } password_confirmation { 'password' } end end
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問