简体   繁体   中英

rails namespaced controller test in rspec

My specs working fine in general, but when I try to test my nested controllers I get a weird error. So this code pattern subject(:create_action) { xhr :post, :create, post_id: post.id, post_comment: attributes_for(:post_comment, post_id: post.id, user: @user) } works fine with my controllers that are not nested, but here for my namespaced controller the test it "saves the new task in the db raises the following error:

1) Posts::PostCommentRepliesController when user is logged in POST create with valid attributes saves the new task in the db
     Failure/Error: subject(:create_action) { xhr :post, :create, post_comment_id: post_comment.id, post: attributes_for(:post_comment_reply, post_comment: post_comment, user: @user) }

     ArgumentError:
       wrong number of arguments (4 for 0)

What should I do to make this work? As you see I put my post_comments_controller_spec.rb in the specs/controllers/posts folder and require posts/post_comments_controller , but it doesn't help.

routes.rb

resources :posts do
  resources :post_comments, only: [:create, :update, :destroy, :show], module: :posts
end

spec/controllers/posts/post_comments_controller_spec.rb

require "rails_helper"
require "posts/post_comments_controller"

describe Posts::PostCommentsController do

  describe "when user is logged in" do

    before(:each) do
      login_user
    end

    it "should have a current_user" do
      expect(subject.current_user).to_not eq(nil)
    end

    describe "POST create" do
      let!(:profile) { create(:profile, user: @user) }
      let!(:post) { create(:post, user: @user) } 

      context "with valid attributes" do
        subject(:create_action) { xhr :post, :create, post_id: post.id, post_comment: attributes_for(:post_comment, post_id: post.id, user: @user) }

        it "saves the new task in the db" do
          expect{ create_action }.to change{ PostComment.count }.by(1)
        end
      end
    end
  end
end

This is a slightly weird bug. In short, your let!(:post) { ... } ended up overwriting a method called post on the example group that is used to issue a post request.

This happened because when you define a let within a describe , RSpec defines you a method of the same name within the given example group .

describe Post do
  let(:post) { Post.new }

  it "does something" do
    post.do_something
  end
end

xhr method (along with get , post , etc), on the other hand is a helper method for testing from Rails itself. RSpec includes this module with the helper, so that it is available in the tests.

describe PostController, type: :controller do
  let(:comment) { Comment.new }

  it "can post a comment thanks to Rails TestCase::Behaviour" do
    post :create, comment
    # xhr :post, ... under the covers calls for post method
  end
end

So the example group object had a method called post, put when you created the let, RSpec went and created you a method of that same name on the group object and thus overwriting the original (and much needed) post method.

describe PostController, type: :controller do
  let(:post) { Post.new }

  it "mixes up posts" do
    post :create, post # post now refers to the variable, so this will cause an error
  end
end

To easily resolve this issue, I would recommend naming the let(:post) to something else, like new_post for example.

describe PostController, type: :controller do
  let(:new_post) { Post.new }

  it "does not mix up posts" do
    post :create, new_post # no more conflict
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM