2012-01-29 6 views
6

Tôi có ứng dụng Rails (Rails 3.0.10) nơi người dùng có thể có nhiều bài viết và nơi người dùng có thể để lại nhận xét về bài viết. Nhận xét được thực hiện trên trang hiển thị bài viết.Kiểm tra RSpec để tạo hành động của bộ điều khiển cho tài nguyên lồng nhau

Bây giờ tôi muốn thử nghiệm hành động tạo của CommentController, tuy nhiên, tôi có vấn đề về cách gọi phương thức bài với các tham số đúng.

Dưới đây là đoạn code của 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 

Mã RSpec để thử nghiệm hành động này (một số thí nghiệm cho đến nay) như sau:

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 

Cả hai thử nghiệm thất bại, tuy nhiên, do các lý do khác nhau mà tôi không thể khắc phục:

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)>' 

Tôi sẽ rất tuyệt nếu ai đó có thể giúp tôi. Cảm ơn trước!

Cập nhật: Đây là routes.rb Tôi đang sử dụng:

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

Cập nhật: Đây là việc sửa đổi Tôi đã theo nmotts gợi ý:

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 

Và định nghĩa FactoryGirl bình luận:

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

Thật không may, mã chưa hoạt động.

+0

Vui lòng đăng tuyến đường của bạn.rb – lucapette

+0

Tôi đã cập nhật bài đăng của mình bằng các tuyến đường đầy đủ.rb –

Trả lời

18

Đối với tài nguyên lồng nhau, bạn cần phải xây dựng dữ liệu thiết lập và bài đăng theo cách như vậy để xác định bài viết gốc khi đăng nhận xét con.

Một cách tiếp cận là thiết lập liên kết Factory Girl một cách chính xác và sau đó đảm bảo phần tử cha được đặt khi tạo thuộc tính con. Nó sẽ giống như thế này:

Trong nhà máy bình luận:

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

Bằng cách gọi bài báo, và đảm bảo rằng có một nhà máy có giá trị gọi là :article sau đó FactoryGirl sẽ tạo ra một bài báo khi bình luận được tạo ra. Để làm cho các thử nghiệm chảy tốt, chúng ta nên thực sự cụ thể về việc article được sử dụng khi comment được tạo ra, vì vậy giờ đây Nhà máy được đặt ra, chúng tôi sử dụng các thông số sau trong thông số kỹ thuật.

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

Điều này sẽ tạo thuộc tính nhận xét được tự động đính kèm vào @article. Phần cuối cùng sau đó là xây dựng bài đăng, đảm bảo rằng chúng tôi bao gồm phụ huynh và trẻ em.

Khi tài nguyên lồng nhau được đăng, nó sẽ yêu cầu thông số cho cả tài nguyên chính và con. Trong rspec, chúng tôi có thể cung cấp điều này trong bài đăng như sau:

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

Điều này nên liên kết tất cả các phần chính xác.

+0

Cảm ơn nmott, với các giải thích của bạn mọi thứ trở nên rõ ràng hơn với tôi. Thật không may, nó đã không làm việc ra được nêu ra. Tôi đã đăng các thay đổi của tôi theo các đề xuất của bạn ở trên. –

+0

OK, giờ tôi đã hoạt động. Vấn đề là chỉ người dùng đã đăng nhập mới có thể đưa ra nhận xét. Vấn đề là do đó không chỉ làm thế nào để kiểm tra bộ điều khiển lồng nhau, nhưng nó là cần thiết để tạo một người dùng thử và đăng nhập người dùng trước khi kiểm tra bộ điều khiển chú thích lồng nhau. –