RSpec test to create a controller action for a nested resource

I have a Rails application (Rails 3.0.10) where users can have many articles and where users can post comments. Comments are made on the article page.

Now I want to check the create commentController action, however I am having problems calling the post method with the correct parameters.

Here's the comment code of the Controller:

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 code to test this action (some experiments so far):

 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 

Both tests do not work, however due to various reasons that I cannot fix:

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

It would be great if someone could help me. Thanks in advance!

Update: here route.rb I use:

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

Update: here is the modification I made according to nnotts suggestions:

 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 

And the FactoryGirl definition for comment:

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

Unfortunately, the code is not working yet.

+6
source share
1 answer

For the embedded resource, you need to construct the installation data and the message in such a way as to identify the parent article when publishing the child comment.

One approach is to properly configure Factory Girl associations, and then ensure that the parent is set when creating the child attributes. It will look something like this:

In the factory comment:

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

By invoking the article and making sure that there is a valid Factory called :article , then FactoryGirl will create the article when creating the comment. For the tests to run well, we must be specific about which article used to create the comment , so now that the Factory is in place, we use the following in the specification.

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

This will create comment attributes that are automatically attached to @article. The final part is to build a post, making sure that we include the parent and child.

When a nested resource is hosted, it expects parameters for both the parent resource and the child. In rspec, we can indicate this in the message as follows:

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

This should correctly connect all the parts.

+18
source

Source: https://habr.com/ru/post/907170/


All Articles