ActionController::UrlGenerationError: No route matches (Rspec) - ruby-on-rails

I am getting the following error while testing my CommentsController with RSpec:
ActionController::UrlGenerationError:
No route matches {:action=>"create", :comment=>{:comment=>"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, :controller=>"comments"}
spec/models/comment_spec.rb
RSpec.describe CommentsController, type: :controller do
let!(:user) { create(:user) }
let!(:post1) { create(:post, user: user) }
let!(:comment) { create(:comment, user_id: user.id, post_id: post1.id) }
let!(:comment_attributes) { attributes_for(:comment) }
describe "#create" do
before do
sign_in user
end
it 'save post' do
expect do
post :create, params: { comment: comment_attributes }, session: {}
end.to change(Comment, :count).by(1)
end
it 'if post saves, redirect_to posts page' do
post :create, params: { post: comment_attributes }, session: {}
expect(response).to redirect_to(posts_path)
end
end
end

You have to update your routes.rb file every time you create a new resource (that you want to access via link), otherwise Rails don't know which controller to use with a given URL. Adding a resources :comments line in routes.rb should do it for you.

Related

How to make request specs for rails controller - request not hitting controller method

I am trying to write specs for a controller but my tests don't seem to be hitting the controller action.
My controller:
class CoursesController < ApplicationController
def elearning_course_removal
Rails.logger.warn("warning!")
study_group = StudyGroup.where(id: params[:study_group_id]).first
course = Course.where(id: params[:id]).first
if study_group && course
Workflow.new.remove_elearning_course(study_group, course, current_user.id)
flash[:notice] = t('study_groups.courses.remove_elearning_course.enqueue', course_name: course.name)
else
Rails.logger.error(elearning_removal_error(study_group, course))
flash[:notice] = t('study_groups.courses.remove_elearning_course.enqueue_failure')
end
redirect_to :back
end
end
My test:
require 'spec_helper'
describe CoursesController do
describe '#elearning_course_removal' do
let(:course) { FactoryBot.create(:course_with_files) }
let(:study_group) { FactoryBot.create(:study_group) }
let(:user) { FactoryBot.create(:user) }
let(:params) { {study_group_id: study_group.id, id: course.id, current_user: user} }
it 'logs' do
expect(Rails.logger).to receive(:warn)
# put :elearning_course_removal, params
controller.elearning_course_removal
end
end
Calling the route like this put :elearning_course_removal, params in the spec just fails saying that logger wasn't called. Using the second style, controller.elearning_course_removal will hit my controller method, but it's not standard, and I don't know how I would pass params in this case. Its not a routing issue, as it is identifying the controller variable correctly.
What do I need to be able to write request specs for this controller?
I think that you are not sending properly the params
RSpec.describe CoursesController do
describe "GET elearning_course_removal" do
let(:course) { FactoryBot.create(:course_with_files) }
let(:study_group) { FactoryBot.create(:study_group) }
let(:user) { FactoryBot.create(:user) }
let(:params) { {study_group_id: study_group.id, id: course.id, current_user: user} }
it "logs" do
expect(Rails.logger).to receive(:warn)
put :elearning_course_removal, params: params
end
end
you can pass params like this
params: {study_group_id: study_group.id, id: course.id, current_user: user}
params: params
or directly
put :elearning_course_removal, params: {study_group_id: study_group.id, id: course.id, current_user: user}
what you are doing is this
put :elearning_course_removal, {study_group_id: study_group.id, id: course.id, current_user: user}
params tag is missing which could be the issue

(LikesController#create) expected #count to have changed by 1, but was changed by 0 ,Please teach me a hint

I'm biginer.
I studied Rspec.
I made an implementation that allowed me to do good on my posts.
But on the browser I do the expected move, but the test does not pass.
The destroy action goes through the test, but the create action does not pass the test.
My error is
Failure/Error: expect { post :create, format: :js, params: { post_id: post1.id, id: like.id } }.to change(Like, :count).by(1)
expected #count to have changed by 1, but was changed by 0
My code is
require 'rails_helper'
RSpec.describe LikesController, type: :controller do
let!(:user) { create(:user) }
let!(:post1) { create(:post, user: user) }
let!(:like) { create(:like, user_id: user.id, post_id: post1.id) }
describe "#create" do
before do
sign_in user
end
it "response Ajex" do
post :create, format: :js, params: { post_id: post1.id, id: like.id }
expect(response.content_type).to eq 'text/javascript'
end
it "success like function" do
expect { post :create, format: :js, params: { post_id: post1.id, id: like.id } }.to change(Like, :count).by(1)
end
end
describe "#destroy" do
before do
sign_in user
end
it "response Ajex" do
delete :destroy, format: :js, params: { post_id: post1.id, user_id: user.id, id: like.id }
expect(response.content_type).to eq 'text/javascript'
end
it "delete like function" do
expect { delete :destroy, format: :js, params: { post_id: post1.id, user_id: user.id, id: like.id } }.to change(Like, :count).by(-1)
end
end
end
likes_controller.rb
class LikesController < ApplicationController
def create
#like =
current_user.likes.find_or_create_by(post_id:params[:post_id])
#likes = Like.where(post_id: params[:post_id])
#post = Post.find(params[:post_id])
end
def destroy
like = current_user.likes.find_by(post_id: params[:post_id])
like.destroy
#likes = Like.where(post_id: params[:post_id])
#post = Post.find(params[:post_id])
end
end
I cannot solove this problem.
Please teach me a hint.
You've got an error in your code somewhere, most likely, which is why the Like count fails to increment. First, I'd try and figure out why it isn't incrementing. Since you asked for a hint, here's one way you can split out the "success like function" block:
context "valid" do
before do
post :create, format: :js, params: { post_id: post1.id, id: like.id }
end
it "success" do
# You can inject a binding.pry here if needed
expect(response.status).to eq(200)
end
it "response" do
# You can inject a `binding.pry` here if needed
# You can also inspect the `response.body` with puts if needed
expect(JSON.parse(response.body)).to include(
# You would modify this to match the shape of your response
post: a_hash_including(
like: like.id
)
)
end
end
You'll want to install pry-rails and pry-byebug gems (for inspecting).
The reason behind splitting them up is it makes it easier to determine the issue (you can have a valid response code but not the expected result, for example). This comes with some caveats (it will make for slower tests) but in this example it will make it easier to determine why your post is failing.
The snippet above should help you debug the error; once you fix it you can revert back to your previous method of checking.

Rspec nested controller test not passing params into my controller

I'm having trouble passing my params into a nested route through rspec. I'm using Rails 5 and Rspec 3.5
My spec looks like this:
require 'rails_helper'
describe "POST /api/v1/companies/:company_id/products.json", type: :controller do
let!(:user) { create(:company_user, address: create(:address)) }
let!(:company) { create(:company, company_user: user) }
let!(:product) { create(:product) }
let!(:params) { FactoryGirl.attributes_for(:product) }
before do
#controller = Api::V1::ProductsController.new
end
context "company_user signed in" do
before do
auth_headers = user.create_new_auth_token
request.headers.merge!(auth_headers)
sign_in user
end
it 'creates a new product' do
post :create, { company_id: company.id }, { params: {product: product_params} }
expect(response.status).to eq(200)
expect(Product.count).to eq(1)
end
end
end
and in my controller my params look like this:
[1] pry(#<Api::V1::ProductsController>)> params
=> <ActionController::Parameters {"company_id"=>"1", "controller"=>"api/v1/products", "action"=>"create"} permitted: false>
Does anyone know why my product params are not being passed in?
The first Hash is the params for the test:
Try it:
post :create, { company_id: company.id, product: product_params }

Rspec Create Post with nested parameters

I'm trying to fix some tests that I have written in my comments controller. As of now, with my current tests I get this error:
Failure/Error: #outlet = Outlet.find(params[:comment][:outlet_id])
ActiveRecord::RecordNotFound:
Couldn't find Outlet with 'id'=
Here is an example of some of the tests
describe '#create' do
context 'with valid attributes' do
before :each do
#outlet = FactoryGirl.create(:outlet)
#user = FactoryGirl.create(:user)
#comment_params = FactoryGirl.attributes_for(:comment)
end
let(:create) { post :create, params: { outlet_id: #outlet.id, user_id: #user.id, comment: #comment_params } }
it "creates new comment" do
expect { create }.to change { Comment.count }.by(1)
end
it "increases the post comment count by 1" do
expect { create }.to change { #outlet.comments.count }.by(1)
end
it "increases user comment count by 1" do
expect { create }.to change { #user.comments.count }.by(1)
end
end
end
I'm pretty sure this is happening because of my create statement in my tests
let(:create) { post :create, params: { outlet_id: #outlet.id, user_id: #user.id, comment: #comment_params } }
Here is my comments controller create action
def create
#outlet = Outlet.find(params[:comment][:outlet_id])
#comment = #outlet.comments.build(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to(#outlet)
end
end
I'm pretty sure it is not working, because the outlet_id that it is looking for is a nested parameter inside of the comments parameter. How would I fix my rspec test to have it look for a nested parameter?
Just pass your params as arguments to the post call, nesting as necessary, e.g.:
post :create, user_id: #user.id, comment: { outlet_id: #outlet.id }

Tests for updating avatar in RSpec

I want to test this update action:
def update
#player = Player.find(params[:id])
begin
#player.update_attributes(player_params_avatar)
flash[:success] = "Avatar updated"
redirect_to player_path(#player)
rescue ActionController::ParameterMissing => e
flash[:danger] = "First choose avatar file"
redirect_to :back
end
end
def player_params_avatar
params.require(:player).permit(:avatar)
end
I'm thinking about something like:
describe "PUT #update" do
let!(:player1) { FactoryGirl.create :player }
context "when data is valid" do
it "update avatar" do
avatar = File.new(Rails.root + 'spec/factories/images/1.png')
put :update, player: { id: player1.id, avatar: avatar }
expect(flash[:success]).to be_present
end
end
But in RSpec console I get the error:
Failure/Error: put :update, player: { id: player1.id, avatar: avatar }
ActionController::UrlGenerationError:
No route matches {:action=>"update", :controller=>"players", :player=>{:id=>"1", :avatar=>"#<File:0xaed5b84>"}}
Why this code rises this error? There is specific player id so should be also correct url eg.
http://localhost:3000/players/1/edit
EDIT: I'm using paperclip gem for avatars.
My routes file:
Rails.application.routes.draw do
get 'pages/about'
get 'matches/new'
root 'pages#home'
resources :players
resources :matches
get '*path' => redirect('/')
end
I resolved the problem myself.
The tricky line was:
put :update, player: { id: player1.id, avatar: avatar }
I changed it to:
put :update, id: player1.id, player: { avatar: file }
And test passes.

Resources