Reputation: 2900
I'm trying to test the 'destroy' action for my nested comments controller.
User model has_many :comments, dependent: :destroy
Movie model has_many :comments, dependent: :destroy
Comments model belongs_to :user and :movie
Here is my comments controller
def create
@comment = @movie.comments.new(comment_params.merge(user: current_user))
if @comment.save
flash[:notice] = 'Comment successfully added'
redirect_to @movie
else
flash.now[:alert] = 'You can only have one comment per movie'
render 'movies/show'
end
end
def destroy
@comment = @movie.comments.find(params[:id])
if @comment.destroy
flash[:notice] = 'Comment successfully deleted'
else
flash[:alert] = 'You are not the author of this comment'
end
redirect_to @movie
end
private
def comment_params
params.require(:comment).permit(:body)
end
def set_movie
@movie = Movie.find(params[:movie_id])
end
Of course there is also before_action :set_movie, only: %i[create destroy]
at the top.
Here are my specs, I'm using FactoryBot and all factories works fine in other examples so I think the issue is somewhere else.
describe "DELETE #destroy" do
let(:user) { FactoryBot.create(:user) }
let(:movie) { FactoryBot.create(:movie) }
before do
sign_in(user)
end
it "deletes comment" do
FactoryBot.create(:comment, movie: movie, user: user)
expect do
delete :destroy, params { movie_id: movie.id }
end.to change(Comment, :count).by(-1)
expect(response).to be_successful
expect(response).to have_http_status(:redirect)
end
end
I've got an error ActionController::UrlGenerationError: No route matches {:action=>"destroy", :controller=>"comments", :movie_id=>1}
I think my address in specs destroy action is wrong but how to define it in a good way?
Upvotes: 5
Views: 7482
Reputation: 43
Somehow I was never able to pass an ID, either in params {} or as a direct argument following "delete". Instead, I made it work like so:
it "DELETE will cause an exception" do
delete "/comments/#{comment.id}"
expect { comment.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
Upvotes: 0
Reputation: 51
I want to contribute here with one more approach. Sometimes you have to be sure that you've deleted the exact instance (comment_1, not comment_2).
it "deletes comment" do
comment_1 = FactoryBot.create(:comment, movie: movie, user: user)
comment_2 = FactoryBot.create(:comment, movie: movie, user: user)
delete :destroy, params { id: comment_1.id, movie_id: movie.id }
expect { comment_1.reload }.to raise_error(ActiveRecord::RecordNotFound)
# ...
end
Upvotes: 4
Reputation: 23327
You need to specify id of a comment you want to remove:
it "deletes comment" do
comment = FactoryBot.create(:comment, movie: movie, user: user)
expect do
delete :destroy, params { id: comment.id, movie_id: movie.id }
end.to change(Comment, :count).by(-1)
# ...
end
Upvotes: 8