我有一个Rails应用程序(Rails 3.0.10),其中用户可以有很多文章,用户可以在文章上发表评论。评论在文章显示页面上进行。
现在我想测试一下CommentsController的创建动作,但是,我遇到了使用正确的参数调用post方法的问题。
以下是CommentsController的代码:
class CommentsController < ApplicationController
# create a comment and bind it to an article and a user
def create
@article = Article.find(params[:article_id])
@user = User.find(@article.user_id)
@comment = @article.comments.build(params[:comment])
@comment.user_id = current_user.id
commenters = []
@article.comments.each {
|comment|
commenters << User.find(comment.user_id)
}
commenters.uniq!
respond_to do |format|
if @comment.save
#Notify user who offers article on new comment, else notify the commenters
if @article.user_id != @comment.user_id
UserMailer.new_article_comment_email(@user, @comment).deliver
else
commenters.each {
|commenter|
UserMailer.new_article_comment_email(commenter, @comment).deliver
}
end
format.html {
redirect_to(@article)
flash[:notice] = t(:comment_create_success)
}
else
format.html {
redirect_to(@article)
flash[:error] = t(:comment_create_error)
}
end
end
end
end
用于测试此操作的RSpec代码(到目前为止的一些实验)如下:
require 'spec_helper'
require 'ruby-debug'
describe CommentsController do
render_views
describe "POST 'create'" do
before(:each) do
@user = FactoryGirl.create(:user)
@article = FactoryGirl.build(:article)
@article.user_id = @user.id
@article.save
@article_attributes = FactoryGirl.attributes_for(:article)
@comment_attributes = FactoryGirl.attributes_for(:comment)
end
it "should create a new comment" do
expect {
post :create, :comment => @comment_attributes
}.to change(Comment, :count).by(1)
end
it "should create a new comment, redirect to the article show page of this comment and notify the user on successful saving of the comment" do
post :create, :comment => @comment_attributes, :article_id => @article.id.to_s, :user_id => @user.id.to_s
flash[:notice].should_not be_nil
response.should redirect_to(article_path(@article))
end
end
end
但是,由于我无法修复的不同原因,这两项测试都失败了:
Failures:
1) CommentsController POST 'create' should create a new comment
Failure/Error: post :create, :comment => @comment_attributes
ActionController::RoutingError:
No route matches {:comment=>{:body=>"This is the body text of a comment"}, :controller=>"comments", :action=>"create"}
# ./spec/controllers/comments_controller_spec.rb:22:in `block (4 levels) in <top (required)>'
# ./spec/controllers/comments_controller_spec.rb:21:in `block (3 levels) in <top (required)>'
2) CommentsController POST 'create' should create a new comment, redirect to the article show page of this comment and notify the user on successful saving of the comment
Failure/Error: post :create, :comment => @comment_attributes, :article_id => @article.id.to_s, :user_id => @user.id.to_s
RuntimeError:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
# ./app/controllers/comments_controller.rb:8:in `create'
# ./spec/controllers/comments_controller_spec.rb:27:in `block (3 levels) in <top (required)>'
如果有人可以帮助我,我会很棒。提前谢谢!
更新:这是我正在使用的routes.rb:
Cinderella::Application.routes.draw do
# The priority is based upon order of creation:
# first created -> highest priority.
# Sample of regular route:
# match 'products/:id' => 'catalog#view'
# Keep in mind you can assign values other than :controller and :action
# Sample of named route:
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
# This route can be invoked with purchase_url(:id => product.id)
match '/signup', :to => 'users#new'
match '/signin', :to => 'sessions#new'
match '/signout', :to => 'sessions#destroy'
match '/home', :to => 'pages#home'
match '/about', :to => 'pages#about'
match '/faq', :to => 'pages#faq'
match '/howitworks_sellers', :to => "pages#howitworks_sellers"
match '/howitworks_buyers', :to => "pages#howitworks_buyers"
match '/contact', :to => 'pages#contact'
match '/articles/:id/ratings', :to => 'ratings#destroy'
# Sample resource route (maps HTTP verbs to controller actions automatically):
# resources :products
resources :articles do
resources :comments, :only => [:create, :destroy]
end
resources :ratings
resources :ratings do
collection do
post 'destroy'
end
end
resources :users do
resources :articles
end
resources :sessions, :only => [:new, :create, :destroy]
# Sample resource route with options:
# resources :products do
# member do
# get 'short'
# post 'toggle'
# end
#
# collection do
# get 'sold'
# end
# end
# Sample resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
# Sample resource route with more complex sub-resources
# resources :products do
# resources :comments
# resources :sales do
# get 'recent', :on => :collection
# end
# end
# Sample resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
# You can have the root of your site routed with "root"
# just remember to delete public/index.html.
root :to => "pages#home"
# See how all your routes lay out with "rake routes"
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
# match ':controller(/:action(/:id(.:format)))'
end
#== Route Map
# Generated on 14 Dec 2011 14:24
#
# signin /signin(.:format) {:controller=>"sessions", :action=>"new"}
# signout /signout(.:format) {:controller=>"sessions", :action=>"destroy"}
# home /home(.:format) {:controller=>"pages", :action=>"home"}
# about /about(.:format) {:controller=>"pages", :action=>"about"}
# faq /faq(.:format) {:controller=>"pages", :action=>"faq"}
# articles GET /articles(.:format) {:action=>"index", :controller=>"articles"}
# POST /articles(.:format) {:action=>"create", :controller=>"articles"}
# new_article GET /articles/new(.:format) {:action=>"new", :controller=>"articles"}
# edit_article GET /articles/:id/edit(.:format) {:action=>"edit", :controller=>"articles"}
# article GET /articles/:id(.:format) {:action=>"show", :controller=>"articles"}
# PUT /articles/:id(.:format) {:action=>"update", :controller=>"articles"}
# DELETE /articles/:id(.:format) {:action=>"destroy", :controller=>"articles"}
# user_articles GET /users/:user_id/articles(.:format) {:action=>"index", :controller=>"articles"}
# POST /users/:user_id/articles(.:format) {:action=>"create", :controller=>"articles"}
# new_user_article GET /users/:user_id/articles/new(.:format) {:action=>"new", :controller=>"articles"}
# edit_user_article GET /users/:user_id/articles/:id/edit(.:format) {:action=>"edit", :controller=>"articles"}
# user_article GET /users/:user_id/articles/:id(.:format) {:action=>"show", :controller=>"articles"}
# PUT /users/:user_id/articles/:id(.:format) {:action=>"update", :controller=>"articles"}
# DELETE /users/:user_id/articles/:id(.:format) {:action=>"destroy", :controller=>"articles"}
# users GET /users(.:format) {:action=>"index", :controller=>"users"}
# POST /users(.:format) {:action=>"create", :controller=>"users"}
# new_user GET /users/new(.:format) {:action=>"new", :controller=>"users"}
# edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
# user GET /users/:id(.:format) {:action=>"show", :controller=>"users"}
# PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
# DELETE /users/:id(.:format) {:action=>"destroy", :controller=>"users"}
# sessions POST /sessions(.:format) {:action=>"create", :controller=>"sessions"}
# new_session GET /sessions/new(.:format) {:action=>"new", :controller=>"sessions"}
# session DELETE /sessions/:id(.:format) {:action=>"destroy", :controller=>"sessions"}
# root /(.:format) {:controller=>"pages", :action=>"home"}
更新:这是我根据nmotts建议所做的修改:
require 'spec_helper'
require 'ruby-debug'
describe CommentsController do
render_views
describe "POST 'create'" do
before(:each) do
@user = FactoryGirl.create(:user)
@article = FactoryGirl.build(:article)
@article.user_id = @user.id
@article.save
@comment_attributes = FactoryGirl.attributes_for(:comment, :article_id => @article)
end
it "should create a new comment" do
post :create, :article_id => @article.id.to_s, :comment => @comment_attributes
end
end
end
FactoryGirl的评论定义:
factory :comment do
body "This is the body text of a comment"
article
end
不幸的是,代码还没有运行。
答案 0 :(得分:18)
对于嵌套资源,您需要构建设置数据和帖子,以便在发布子评论时识别父文章。
一种方法是正确设置Factory Girl关联,然后确保在创建子属性时设置父元素。它看起来像这样:
在评论工厂中:
FactoryGirl.define do
Factory :comment do
comment "My comment"
article
end
end
通过调用文章,并确保有一个名为:article
的有效工厂,FactoryGirl将在创建评论时创建一篇文章。为了使测试顺利进行,我们应该具体说明在创建article
时使用哪个comment
,所以现在工厂就位,我们在规范中使用以下内容。
@comment_attributes = FactoryGirl.attributes_for(:comment, :article_id => @article)
这将构建自动附加到@article的注释属性。最后一部分是构建帖子,确保我们包括父母和孩子。
当发布嵌套资源时,它需要父资源和子资源的参数。在rspec中,我们可以在帖子中提供如下内容:
post :create, :article_id => @article, :comment => @comment_attributes
这应该正确地链接所有部分。