ActiveRecord :: RecordNotFound:找不到没有ID的酒店

时间:2015-06-01 06:48:06

标签: ruby-on-rails ruby activerecord rspec

我在测试ratings_controller时遇到了这些错误。

1) RatingsController create action creates a rating if validations pass
     Failure/Error: post :create, rating: {value: 4, user_id: user, hotel_id: hotel}
     ActiveRecord::RecordNotFound:
       Couldn't find Hotel without an ID
     # ./app/controllers/ratings_controller.rb:6:in `create'
     # ./spec/controllers/ratings_controller_spec.rb:12:in `block (4 levels) in <top (required)>'
     # ./spec/controllers/ratings_controller_spec.rb:11:in `block (3 levels) in <top (required)>'

  2) RatingsController create action does not create rating if validations fail
     Failure/Error: post :create, rating: {value: 3}
     ActiveRecord::RecordNotFound:
       Couldn't find Hotel without an ID
     # ./app/controllers/ratings_controller.rb:6:in `create'
     # ./spec/controllers/ratings_controller_spec.rb:17:in `block (3 levels) in <top (required)>'

  3) RatingsController update action updates rating if validations ok
     Failure/Error: patch :update, value: 3, user_id: user.id, hotel_id: hotel.id
     ActionController::UrlGenerationError:
       No route matches {:action=>"update", :controller=>"ratings", :hotel_id=>"1", :user_id=>"2", :value=>"3"}
     # ./spec/controllers/ratings_controller_spec.rb:25:in `block (3 levels) in <top (required)>'

我不知道他们来自哪里。如果可以,请帮帮我。 我的ratings_controller:

class RatingsController < ApplicationController
  #before_action :signed_in_user
  before_filter :authenticate_user!

  def create
    @hotel = Hotel.find(params[:hotel_id])
    @rating = Rating.new(params[:rating])
    @rating.hotel_id = @hotel.id
    @rating.user_id = current_user.id
    if @rating.save
        redirect_to hotel_path(@hotel), :notice => "Your rating has been saved"
    end
  end

  def update
    @hotel = Hotel.find(params[:hotel_id])
    #@rating = current_user.ratings.find(@hotel.id)
    @rating = Rating.find_by_hotel_id(@hotel.id)
    if @rating.update_attributes(params[:rating])
        redirect_to hotel_path(@hotel), :notice => "Your rating has been updated"
    end
  end

end

我的ratings_controller_spec.rb:

require "spec_helper"

describe RatingsController do
  let(:rating) { FactoryGirl.create(:rating) }
  let(:user) { FactoryGirl.create(:user) }
  let(:hotel) { FactoryGirl.create(:hotel) }

  describe "create action" do
    before { sign_in rating.user }
    it "creates a rating if validations pass" do
      expect {
          post :create, rating: {value: 4, user_id: user, hotel_id: hotel} 
        }.to change(Rating, :count).by(1)
    end

    it "does not create rating if validations fail" do
      post :create, rating: {value: 3} 
      expect(response).to redirect_to(hotel_path(hotel))
    end
  end

  describe "update action" do
    before { sign_in hotel.user }
    it "updates rating if validations ok" do
      patch :update, value: 3, user_id: user.id, hotel_id: hotel.id
      rating.reload
      expect(rating.value).to eq(3);
    end

    it "updates rating if validations fail" do

    end
  end
end

特别是第三个错误使我感到困惑,因为rake routes向我显示了评级更新操作的可用路径。

PATCH  /hotels/:id(.:format)       hotels#update
PUT    /hotels/:id(.:format)       hotels#update

谢谢!

Rails - 4.0.8 Ruby - 1.9.3p551

更新1: 抱歉路线。我的秘密只是复制了错误的行。

rating PATCH  /ratings/:id(.:format)    ratings#update
        PUT    /ratings/:id(.:format)    ratings#update

对我来说似乎没问题,如果我启动服务器并手动测试它就可以正常工作。

1 个答案:

答案 0 :(得分:1)

关于id问题:

@hotel = Hotel.find(params[:hotel_id])期望params哈希值具有顶级hotel_id密钥。从测试中调用create方法时,将hotel_id嵌套在ratings键内:

post :create, rating: {value: 4, user_id: user, hotel_id: hotel}

因此params[:hotel_id]为零。您需要将hotel_id添加为顶级密钥:

post :create, rating: {value: 4, user_id: user, hotel_id: hotel}, hotel_id: hotel

或者,您可以将嵌套的hotel_id直接传递给find方法:

@hotel = Hotel.find(params[:rating][:hotel_id])

关于路线问题:

您的路线已映射到HotelsController(在rake routes输出中显示为hotels#update),但您正在测试RatingsController,因此您获得了

No route matches {:action=>"update", :controller=>"ratings", :hotel_id=>"1", :user_id=>"2", :value=>"3"}

更新您的路线以转到RatingsController的{​​{1}}方法,而不是update,因此您将拥有HotelsController路线。

<强>更新

在这种情况下,路线预计网址中的ratings#update参数:id,您未提供该参数:

/ratings/:id

要么提供您想要更新的特定评级ID,要么更改您的路线不要求patch :update, value: 3, user_id: user.id, hotel_id: hotel.id参数,因为您无论如何通过酒店找到评级,所以它们看起来像

id

而不是

patch '/ratings', to 'ratings#update'

如果您使用patch '/ratings/:id', to 'ratings#update'定义路线,则可以将特定路线置于其上方,因此路由器将首先点击该路线。一个好主意也是排除它:resources :ratings。或者只使用collection routes。有关详细信息,请参阅this question