我正在写一个电子商务网站,我遇到了一个测试问题。我对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”。我把它列在控制器参数中,不知道从哪里开始。
答案 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
答案 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