0

I have researched and seen a similar question (Rspec: Testing nested destroy action) but was unable to get the solution to work for me. In the app I am working on, we have an Articles model and users can Follow Articles.

I have used the acts_as_follower gem to implement this, so Article model has a 'followers' method that returns and array of users following that article.

Here is the relevant code:

follows_controller

class FollowsController < ApplicationController
  def create
    @article = Article.find(params[:article_id])
    current_user.follow(@article)
    redirect_to @article
  end

  def destroy
    @article = Article.find(params[:article_id])    
    current_user.stop_following(@article)
    redirect_to @article
  end
end

routes.rb

    get '/articles/:id/history', to: 'articles#history', as: :articles_history
  post '/articles/:id/undo', to: 'articles#undo', as: :undo
  resources :articles do
    resources :follows, :only => [:create, :destroy]
  end

schema.rb

create_table "follows", force: :cascade do |t|
    t.integer  "followable_id",                   null: false
    t.string   "followable_type",                 null: false
    t.integer  "follower_id",                     null: false
    t.string   "follower_type",                   null: false
    t.boolean  "blocked",         default: false, null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

spec/controllers/follows_controller_spec.rb updated per @Peter's comment below

  describe '#DELETE destroy' do
it 'deletes a follow' do
  @article = FactoryGirl.create(:article)
  follow = FactoryGirl.create(:follow, followable_id: @article.id)
  # Also, test if the action really deletes a comment.
  expect{delete :destroy, id: follow.id, article_id: @article.id}.
  to change{@article.followers.count}.by(-1)
end

end

rake routes:

article_follows POST   /articles/:article_id/follows(.:format)          follows#create
article_follow DELETE /articles/:article_id/follows/:id(.:format)      follows#destroy

Here is the error I get on running the test:

Failures:

1) FollowsController#DELETE destroy deletes a follow
     Failure/Error: expect{delete :destroy, id: follow.id, followable_id: @article.id}.
     ActionController::UrlGenerationError:
       No route matches {:action=>"destroy", :controller=>"follows", :followable_id=>"2", :id=>"2"}

Adding Factory and new error based on @PeterAlfvin's suggestion below:

here is my factory:

FactoryGirl.define do
  factory :follow do |f|
    f.follower_id 1
    f.followable_id 1
    f.followable_type "Article"
    f.follower_type "User"
  end
end

and here is the current error:

  1) FollowsController#DELETE destroy deletes a follow
     Failure/Error: expect{delete :destroy, id: follow.id, article_id: @article.id}.
     NoMethodError:
       undefined method `stop_following' for nil:NilClass
     # ./app/controllers/follows_controller.rb:10:in `destroy'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/metal/implicit_render.rb:4:in `send_action'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/abstract_controller/base.rb:198:in `process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/metal/rendering.rb:10:in `process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/abstract_controller/callbacks.rb:20:in `block in process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/callbacks.rb:117:in `call'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/callbacks.rb:117:in `call'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/callbacks.rb:555:in `block (2 levels) in compile'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/callbacks.rb:505:in `call'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/callbacks.rb:505:in `call'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/callbacks.rb:92:in `_run_callbacks'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/callbacks.rb:776:in `_run_process_action_callbacks'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/callbacks.rb:81:in `run_callbacks'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/abstract_controller/callbacks.rb:19:in `process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/metal/rescue.rb:29:in `process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/metal/instrumentation.rb:32:in `block in process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/notifications.rb:164:in `block in instrument'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
     # ./.bundle/gems/ruby/2.2.0/gems/activesupport-4.2.1/lib/active_support/notifications.rb:164:in `instrument'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/metal/instrumentation.rb:30:in `process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/searchkick-0.8.7/lib/searchkick/logging.rb:107:in `process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/activerecord-4.2.1/lib/active_record/railties/controller_runtime.rb:18:in `process_action'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/abstract_controller/base.rb:137:in `process'
     # ./.bundle/gems/ruby/2.2.0/gems/actionview-4.2.1/lib/action_view/rendering.rb:30:in `process'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/test_case.rb:632:in `process'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/test_case.rb:65:in `process'
     # ./.bundle/gems/ruby/2.2.0/gems/devise-3.4.1/lib/devise/test_helpers.rb:19:in `block in process'
     # ./.bundle/gems/ruby/2.2.0/gems/devise-3.4.1/lib/devise/test_helpers.rb:72:in `catch'
     # ./.bundle/gems/ruby/2.2.0/gems/devise-3.4.1/lib/devise/test_helpers.rb:72:in `_catch_warden'
     # ./.bundle/gems/ruby/2.2.0/gems/devise-3.4.1/lib/devise/test_helpers.rb:19:in `process'
     # ./.bundle/gems/ruby/2.2.0/gems/actionpack-4.2.1/lib/action_controller/test_case.rb:532:in `delete'
     # ./spec/controllers/follows_controller_spec.rb:22:in `block (4 levels) in <top (required)>'
     # ./spec/controllers/follows_controller_spec.rb:22:in `block (3 levels) in <top (required)>'
     # ./spec/spec_helper.rb:57:in `block (3 levels) in <top (required)>'
     # ./.bundle/gems/ruby/2.2.0/gems/database_cleaner-1.3.0/lib/database_cleaner/generic/base.rb:15:in `cleaning'
     # ./.bundle/gems/ruby/2.2.0/gems/database_cleaner-1.3.0/lib/database_cleaner/base.rb:92:in `cleaning'
     # ./.bundle/gems/ruby/2.2.0/gems/database_cleaner-1.3.0/lib/database_cleaner/configuration.rb:86:in `block (2 levels) in cleaning'
     # ./.bundle/gems/ruby/2.2.0/gems/database_cleaner-1.3.0/lib/database_cleaner/configuration.rb:87:in `call'
     # ./.bundle/gems/ruby/2.2.0/gems/database_cleaner-1.3.0/lib/database_cleaner/configuration.rb:87:in `cleaning'
     # ./spec/spec_helper.rb:56:in `block (2 levels) in <top (required)>'
Community
  • 1
  • 1
user2799827
  • 1,077
  • 3
  • 18
  • 54
  • where do you define `delete_request` ? – Mohammad AbuShady May 09 '15 at 13:00
  • I copied and pasted a previous error by mistake. corrected above. – user2799827 May 09 '15 at 13:35
  • try replacing `followable_id` with `article_id` – Mohammad AbuShady May 09 '15 at 13:36
  • changed to this: describe '#DELETE destroy' do it 'deletes a follow' do @article = FactoryGirl.create(:article) follow = FactoryGirl.create(:follow, article_id: @article.id) # Also, test if the action really deletes a comment. expect{delete :destroy, id: follow.id, article_id: @article.id}. to change{@article.followers.count}.by(-1) end end get error: NoMethodError: undefined method `article_id=' for # – user2799827 May 09 '15 at 13:40
  • edited the question to include the schema for Follows table. – user2799827 May 09 '15 at 13:44
  • @PeterAlfvin not sure that the routing question has been resolved. There is no article_id method to the Follow model, which is why I edited the question to include the schema. substituting article_id does just returns a no method error. – user2799827 May 09 '15 at 15:54
  • Indeed, you're right. I removed my comment and posted an answer. – Peter Alfvin May 10 '15 at 00:33

1 Answers1

0

I think the routing error was indeed due to specifying followable_id rather than article_id as implied by the comment you received. The subsequent no method error, however, was due to making that change in the factory call as well. The following is what you should try:

describe '#DELETE destroy' do
  it 'deletes a follow' do
    @article = FactoryGirl.create(:article)
    follow = FactoryGirl.create(:follow, followable_id: @article.id)
    # Test if the action really deletes a follow.
    expect{delete :destroy, id: follow.id, article_id: @article.id}.
    to change{@article.followers.count}.by(-1)
  end
end 
Peter Alfvin
  • 28,599
  • 8
  • 68
  • 106
  • Peter, thanks for this. I have implemented but now get an error which I think is suggesting that i haven't created a valid article object: undefined method `stop_following' for nil:NilClass. stop_following is a method provided by the gem. – user2799827 May 10 '15 at 10:11
  • No, `stop_following` is being called on `current_user`, so you apparently are not logged in. – Peter Alfvin May 11 '15 at 03:30