在RSpec

时间:2017-04-17 23:27:51

标签: ruby-on-rails ruby rspec

我正在写一个电子商务网站,我遇到了一个测试问题。我对create动作的测试如下所示:

require 'rails_helper'

RSpec.describe ReviewsController, type: :controller do
    describe "POST #create" do
        before(:each) do
            user = FactoryGirl.create(:user)
            login user
            @product = FactoryGirl.create(:product)
            post :create, params: { blurb: "Lorem ipsum something or other" }
        end

        it "renders the product path" do
            expect(response).to redirect_to(product_path(@product))
        end
    end
end

“登录用户”行有效,我用byebug确认(有一个有效的用户和会话)。在这个迭代中,我得到No route匹配的错误{:action =>“create”,:blurb =>“Lorem ipsum something or other”,:controller =>“reviews”} ,即使我的佣金路线看起来像这样:

                  Prefix Verb   URI Pattern                                 Controller#Action
                root GET    /                                           landings#index
    new_user_session GET    /users/sign_in(.:format)                    devise/sessions#new
        user_session POST   /users/sign_in(.:format)                    devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format)                   devise/sessions#destroy
   new_user_password GET    /users/password/new(.:format)               devise/passwords#new
  edit_user_password GET    /users/password/edit(.:format)              devise/passwords#edit
       user_password PATCH  /users/password(.:format)                   devise/passwords#update
                     PUT    /users/password(.:format)                   devise/passwords#update
                     POST   /users/password(.:format)                   devise/passwords#create
cancel_user_registration GET    /users/cancel(.:format)                     devise/registrations#cancel
new_user_registration GET    /users/sign_up(.:format)                    devise/registrations#new
edit_user_registration GET    /users/edit(.:format)                       devise/registrations#edit
user_registration PATCH  /users(.:format)                            devise/registrations#update
                     PUT    /users(.:format)                            devise/registrations#update
                     DELETE /users(.:format)                            devise/registrations#destroy
                     POST   /users(.:format)                            devise/registrations#create
     product_reviews POST   /products/:product_id/reviews(.:format)     reviews#create
  new_product_review GET    /products/:product_id/reviews/new(.:format) reviews#new
             product GET    /products/:id(.:format)                     products#show
             jewelry GET    /jewelry(.:format)                          products#jewelry
            clothing GET    /clothing(.:format)                         products#clothing
         decorations GET    /decorations(.:format)                      products#decorations

当我将帖子行更改为如下所示:     post:create,user:user,params:{blurb:“Lorem ipsum something or other”} 我得到“密钥不存在:用户”。这是我的模特:

class Review < ApplicationRecord
    belongs_to :user
    belongs_to :product

    validates_presence_of :blurb
end

我的控制器:

class ReviewsController < InheritedResources::Base
    respond_to :html
    belongs_to :product
    actions :new

    def create
        @review = Review.new(review_params)
        @product = Product.find(params[:product_id])
        if user_signed_in?
            @review.user_id = current_user.id
            @review.product_id = params[:product_id]
            if @review.save!
                redirect_to product_path(@product)
            else
                render :new
            end
        else
            redirect_to new_user_session_path
        end
    end

    private

    def review_params
        params.require(:review).permit(:blurb, :utf8, :authenticity_token, :commit)
    end
end

我的routes.rb:

Rails.application.routes.draw do
    root 'landings#index'
    devise_for :users
    resources :products, only: [:show] do
        resources :reviews, only: [:new, :create]
    end
    get '/jewelry', to: 'products#jewelry', as: :jewelry
    get '/clothing', to: 'products#clothing', as: :clothing
    get '/decorations', to: 'products#decorations', as: :decorations
end

我正在使用Ruby 2.4.0和Rails 5.1,任何帮助都将不胜感激。

// EDIT

我将FactoryGirl :: Syntax :: Methods添加到我的RSpec配置中,并重构为使用let和product_id,如下所示:

require 'rails_helper'

RSpec.describe ReviewsController, type: :controller do
    let(:user) { create(:user) }
    let(:product) { create(:product) }

    describe "GET #new" do
        before(:each) do
            get :new, params: { product_id: product.id }
        end

        it "renders the new template" do
            expect(response).to render_template(:new)
        end

        it "returns a 200 status code" do
            expect(response).to have_http_status(200)
        end
    end

    describe "POST #create" do
        before do
            login user
        end

        context "with valid attributes" do
            let(:action) do
                post :create, product_id: product, params: { review: FactoryGirl.attributes_for(:review) }
            end

            it "creates a review" do 
                expect { action }.to change(product.reviews, :count).by(+1)
            end

            it "redirects to the review" do
                action
                expect(response).to redirect_to product.reviews.last
            end
        end
    end
end

我没有收到错误“unknown keyword:product_id”。我把它列在控制器参数中,不知道从哪里开始。

2 个答案:

答案 0 :(得分:2)

根据您的行动路线/products/:product_id/reviews(.:format),您需要传递product_id

因此请将您的规范更改为:

post :create, product_id: @product.id, params: { blurb: "Lorem ipsum something or other" }

您可能需要在actions :new中将actions :create更改为ReviewsController

此外,您可以在config.include FactoryGirl::Syntax::Methods添加rails_helper.rb行,这样每次调用FG方法时都不需要写FactoryGirl

RSpec.configure do |config|
  ...
  config.include FactoryGirl::Syntax::Methods
  ...
end

这是link to FG docs about that

答案 1 :(得分:2)

您的路线需要product_id参数。除此之外,你的测试非常需要适当的结构。如果要查看如何测试标准crud控制器的示例,可以在rails中使用scaffold命令。

require 'rails_helper'

RSpec.describe ReviewsController, type: :controller do

  # use let to setup given variables
  let(:user) {  FactoryGirl.create(:user) }
  let(:product) {  FactoryGirl.create(:product) }

  describe "POST #create" do
    before do
      login user
    end

    context "with valid attributes" do
      let(:action) do
        post :create, params: { product_id: product, review: FactoryGirl.attributes_for(:review) }
      end

      it "creates a review" do 
        expect { action }.to change(product.reviews, :count).by(+1)
      end

      it "redirects to the review" do
        action
        expect(response).to redirect_to product.reviews.last
      end
    end

    context "with invalid attributes" do
      # ...
    end
  end
end

您应该将身份验证移动到之前的过滤器并剪切复制,您可以将控制器压缩很多:

# call this whatever you want but DRY it out.
class ApplicationController < InheritedResources::Base
  before_action :authenticate!

  def authenticate!
    redirect_to new_user_session_path and return false unless user_signed_in?
  end 
end 

class ReviewsController < ApplicationController
  respond_to :html
  actions :new

  def create
    @product = Product.find(params[:product_id])
    @review = @product.reviews.new(review_params) do |r|
      r.user = current_user
    end
    # don't use .save! here - it will raise an exception
    # if the record is not valid
    if @review.save
      redirect_to product_path(@product)
    else
      render :new
    end
  end

  private

  def review_params
    # only permit the params that actually should be assigned to the model!
    params.require(:review).permit(:blurb) 
  end
end