2012-01-29 20 views
6

Tengo una aplicación Rails (Rails 3.0.10) donde los usuarios pueden tener muchos artículos, y donde los usuarios pueden dejar comentarios sobre los artículos. Los comentarios se hacen en la página de la demostración del artículo.Prueba RSpec para crear la acción de un controlador para un recurso anidado

Ahora quiero probar la acción create del CommentsController, sin embargo, tengo problemas para invocar el método post con los parámetros correctos.

Aquí está el código de la 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 

El código RSpec para probar esta acción (algunos experimentos hasta ahora) es la siguiente:

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 

Ambas pruebas fallan, sin embargo, debido a razones diferentes que no puedo solucionar:

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

Estaría genial si alguien podría ayudarme. ¡Gracias por adelantado!

Actualización: Aquí está la routes.rb estoy usando:

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

Actualización: Aquí está la modificación que hizo conforme a nmotts sugerencias:

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 

Y la definición factorygirl para hacer comentarios:

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

Desafortunadamente, el código aún no está funcionando.

+0

Por favor, publique sus rutas.rb – lucapette

+0

Actualicé mi publicación con las rutas completas. rb –

Respuesta

18

Para un recurso anidado necesita construir los datos de configuración y la publicación de tal manera que identifique el artículo principal al publicar el comentario secundario.

Un enfoque es configurar las asociaciones de Factory Girl correctamente y luego asegurarse de que el elemento principal esté configurado al crear los atributos secundarios. Se vería algo como esto:

En la fábrica comentario:

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

Llamando artículo, y asegurarse de que hay una fábrica válida llamada :article continuación factorygirl creará un artículo cuando se crea un comentario. Para hacer que las pruebas fluyan bien, en realidad deberíamos especificar qué article se usa cuando se crea el comment, de modo que ahora que la fábrica está en su lugar usamos lo siguiente en la especificación.

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

Esto generará atributos de comentarios que se adjuntan automáticamente a @article. La última pieza es construir la publicación, asegurándonos de incluir al padre y al niño.

Cuando se publica un recurso anidado, espera parámetros para el recurso principal y el secundario. En rspec podemos proporcionar esto en la publicación de la siguiente manera:

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

Esto debería vincular todas las piezas correctamente.

+0

Gracias nmott, con sus explicaciones las cosas se aclaran más para mí. Desafortunadamente, no funcionó aún. Publiqué mis cambios de acuerdo con sus recomendaciones anteriores. –

+0

OK, lo tengo trabajando ahora. El problema es que solo los usuarios que han iniciado sesión pueden hacer comentarios. El problema no era solo cómo probar el controlador anidado, sino que era necesario crear un usuario de prueba y registrar al usuario antes de probar el controlador de comentarios anidados. –

Cuestiones relacionadas