I have rspec test for nested object (comment), it is nested in posts object. Here is the routes.rb:
use_doorkeeper
resources :posts do
resources :comments
end
root "posts#index"
end
Here is part of comments controller:
class CommentsController < ApplicationController
before_action :find_comment, only: [:destroy]
before_action :authenticate_user!, only: [:destroy]
def create
@post = Post.find(params['post_id'])
@comment = @post.comments.create(params[:comment].permit(:name, :comment, :user_id, :best, :file))
respond_to do |format|
format.html { redirect_to post_path(@post) }
format.js
end
end
private
def find_comment
@post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
end
end
And here is rspec test:
require 'rails_helper'
RSpec.describe CommentsController, type: :controller do
let(:user) { create(:user) }
let(:post) { create(:post) }
sign_in_user
setup do
allow(controller).to receive(:current_user).and_return(user)
end
describe 'POST #create' do
context 'with valid attributes' do
let!(:posts) { post :create, params: { post_id: post, comment: attributes_for(:comment) } }
it 'saves the new comment in the database' do
expect {{ comment: attributes_for(:comment) } }.to change(Comment, :count).by(1)
end
end
end
end
Additional factory file for comments:
FactoryBot.define do
factory :comment do
comment { 'Comment' }
name { 'Name' }
post
user_id { create(:user).id }
end
end
Macros to authorize user:
module ControllerMacros
def sign_in_user
before do
@user = create(:user)
@request.env['devise.mapping'] = Devise.mappings[:user]
sign_in @user
end
end
end
So when I run spec test, I got this error:
ArgumentError: wrong number of arguments (given 2, expected 0)
What may be the problem, why can't I run CREATE rspec?
You have a couple of things going on here. The error is because your let(:post)
memoized helper is overriding the name of the post
method. So, when you call post :create ...
it is very unhappy. So, you just need to change the name of your memoized helper to something other than :post
.
The second issue is your expectation will never pass:
expect {{ comment: attributes_for(:comment) } }.to change(Comment, :count).by(1)
When you use the expect {}...to change()
form, RSpec literally checks that what's inside the expect
block drives the specified change. In your case, the code inside the expect block doesn't create a Comment record in the database, it just returns a hash of attributes for a mock Comment so it will never pass. You do create a new Comment at the start of the context block, but it doesn't count since it's outside the expect
block.
One way to fix that is to simply move the POST into the expect block:
RSpec.describe CommentsController, type: :controller do
let(:user) { create(:user) }
let(:faux_post) { create(:post) } // Different name to avoid clash
// ... stuff omitted
describe 'POST #create' do
context 'with valid attributes' do
it 'saves the new comment in the database' do
expect do
post :create, params: { post_id: faux_post, comment: attributes_for(:comment) }
end.to change(Comment, :count).by(1)
end
end
end
end