2012-01-29 26 views
6

私は、ユーザーが多くの記事を持つことができるRailsアプリケーション(Rails 3.0.10)と、ユーザーが記事にコメントを残せる場所を持っています。コメントは記事の表示ページで行います。ネストされたリソースのコントローラの作成アクションのRSpecテスト

今、私は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)>' 

誰かが私を助けることができた。前もって感謝します!

更新:

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"} 

アップデート:ここで私が使用していますroutes.rbをです

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定義:ここで私はnmottsの提案に応じた変更だ

factory :comment do 
    body "This is the body text of a comment" 
    article 
end 

残念ながら、コードはまだ動作していません。

+0

ルートを投稿してください。rb – lucapette

+0

私の投稿を完全なroutes.rbで更新しました –

答えて

18

ネストされたリソースの場合、子コメントを投稿するときに親記事を特定するような方法で設定データと投稿を構築する必要があります。

ファクトリーガールの関連付けを正しく設定し、子属性を作成するときに親要素が設定されていることを確認します。コメントの工場で

:それは次のようになります

FactoryGirl.define do 
    Factory :comment do 
    comment "My comment" 
    article 
    end 
end 

記事を呼び出し、コメントが作成されると、その後FactoryGirl記事を作成します:articleと呼ばれ、有効な工場があることを確認して行うことで。テストがうまくいくためには、commentが作成されたときに実際にarticleが使用されているかどうかを特定する必要があります。したがって、ファクトリが存在するようになりました。

@comment_attributes = FactoryGirl.attributes_for(:comment, :article_id => @article) 

これは、@articleに自動的に付加されるコメント属性を構築します。最後の部分は、親と子を含めることを確認して投稿を作成することです。

ネストされたリソースが送信されると、親リソースと子の両方のparamsが必要です。次のようにRSpecのでは、我々はポストにこれを提供することができます。これは正しくすべての部分をアップリンクする必要があり

post :create, :article_id => @article, :comment => @comment_attributes 

+0

おかげでnmott、あなたの説明がわかりやすくなりました。残念ながら、まだうまくいきませんでした。私はあなたの上記の推奨に従って変更を掲載しました。 –

+0

さて、私は今働いている。問題は、署名されたユーザーだけがコメントをすることができるということでした。したがって、問題はネストされたコントローラをテストするだけでなく、ネストされたコメントコントローラをテストする前にテストユーザを作成し、ユーザをログインさせる必要がありました。 –

関連する問題