2012-01-29 3 views
6

사용자가 많은 기사를 가질 수 있고 사용자가 기사에 대한 의견을 남길 수있는 레일즈 애플리케이션 (Rails 3.0.10)이 있습니다. 의견은 기사 쇼 페이지에서 이루어집니다.중첩 된 리소스에 대한 컨트롤러의 생성 작업에 대한 RSpec 테스트

이제 CommentsController의 생성 동작을 테스트하고 싶지만 올바른 매개 변수로 post 메서드를 호출하는 데 문제가 있습니다.

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 

모두 테스트가 실패하지만, 때문에 :

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에 코드는 다음과 같다 : 여기

는 CommentsController의 코드입니다 내가 해결할 수없는 다른 이유 :

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

중첩 리소스의 경우 하위 주석을 게시 할 때 상위 문서를 식별하는 방식으로 설정 데이터와 게시물을 구성해야합니다.

하나의 접근법은 Factory Girl 연결을 올바르게 설정 한 다음 하위 속성을 만들 때 부모 요소가 설정되어 있는지 확인하는 것입니다. 주석 공장에서

: 그것은 다음과 같이 보일 것이다

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

좋아, 지금 일하고있어. 문제는 사용자 만 로그인 할 수 있다는 것입니다. 따라서 문제는 중첩 된 컨트롤러를 테스트하는 방법 일뿐만 아니라 중첩 된 주석 컨트롤러를 테스트하기 전에 테스트 사용자를 만들고 사용자를 로그인해야했습니다. –

관련 문제