All. I am a beginner developer of Ruby on Rails. There is an error that I have been struggling and not been able to solve.
I am trying to see if an variable #chat_group declared in messages_controller.rb matches another variable declared in message_controller_spec.rb. I have not had any clue to solve this error because I am 100% positive that I am giving what I am supposed to give to as a parameter, which is chat_group_id as the route says.
Does anyone have any insight to solve this problem?
Or has anyone encountered a similar issue before?
If you does or has, could you please give a way of solving this problem?
Any advice would be appreciated! Thanks in advnace!
1) MessagesController#GET index when the user logs in Checking if the variable in index action matches
Failure/Error: get :index, params: index_params
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"messages", :params=>{:chat_group_id=>"522"}}
# ./spec/controllers/messages_controller_spec.rb:13:in `block (4 levels) in <top (required)>'
messages_controller_spec.rb
RSpec.describe MessagesController, type: :controller do
let(:chat_group) { create(:chat_group) }
let(:message) { create(:message) }
let(:index_params) { { chat_group_id: chat_group } }
describe '#GET index' do
context 'when the user logs in' do
login_user
it 'Checking if the variable in index action matches' do
get :index, params: index_params
expect(assigns(:chat_group)).to eq chat_group
end
end
end
end
Route.rb
chat_group_messages GET /chat_groups/:chat_group_id/messages(.:format) messages#index
POST /chat_groups/:chat_group_id/messages(.:format) messages#create
chat_groups GET /chat_groups(.:format) chat_groups#index
POST /chat_groups(.:format) chat_groups#create
new_chat_group GET /chat_groups/new(.:format) chat_groups#new
edit_chat_group GET /chat_groups/:id/edit(.:format) chat_groups#edit
chat_group PATCH /chat_groups/:id(.:format) chat_groups#update
PUT /chat_groups/:id(.:format) chat_groups#update
MessageController.rb
class MessagesController < ApplicationController
before_action :set_chat_group
def index
#chat_groups = current_user.chat_groups
#messages = #chat_group.messages
#message = Message.new
respond_to do |format|
format.html { render :index }
format.json { render json:
#chat_group.messages.includes(:user).map{|x| x.json_api} }
end
end
private
def set_chat_group
#chat_group = ChatGroup.find(params[:chat_group_id])
end
end
Update
I solved the error! I put the way of solving it in the comments below!
I am so sorry. I was looking at a documentation for Rails5 instead of Rails4.
It says that I should pass the chat_group_id by writing like below.
Rails4
before do
get :index, { chat_group_id: chat_group }
end
Not Like this
before do
get :index, params: { chat_group_id: chat_group }
end
This error made me realize how important it is to read the documentation...
For those who put comments and tried to do so, THANK YOU VERY MUCH!
try to change type: controller to type: request?
Also you can use, request type and specify path:
get chat_group_messages_path(chat_group.id), index_params
Cheers
I think You are doing everything right.
but this line : expect(assigns(:group)).to eq group
you expected assigns(:group) but i can't see any variable named group in your index action that you are testing.
also you are expecting that to equal group "eq group", but in your let blocks in the example above no variable named group exists.
So try to do expect(assigns(:chat_groups)).to eq chat_group
the assigns(:chat_groups) uses the same name of the instance variable inside your action, and the eq chat_group uses the name of the variable in the let block
Related
Have been spending some months trying to grok RSpec/TDD. Running into some challenges testing a controller with a nested attribute. I'm a bit fuzzy about how to properly set up and pass the parameters to the controller. I can build the controller so that it actually works as expected in the browser - I just can't seem to reverse engineer a test to confirm that.
Would appreciate any recommendations to a) fix the test below, and b) advise any better ways to do it (e.g. with mocks, stubs, etc).
Here's the basic model structure:
class School < ActiveRecord::Base
has_many :scholarships
end
class Scholarship < ActiveRecord::Base
belongs_to :school
end
I've configured the routes.rb as you'd expect, with:
resources :schools do
resources :scholarships, only: [:new, :create, :destroy]
end
In the controller, #new and #create are pretty standard for a Rails app:
class ScholarshipsController < ApplicationController
def new
#school = School.find(params[:school_id])
#scholarship = #school.scholarships.build
end
def create
#school = School.find(params[:school_id])
#scholarship = #school.scholarships.create(scholarship_params)
if #scholarship.save
flash[:success] = 'Scholarship created!'
redirect_to #school
else
render 'new'
end
end
private
def scholarship_params
params.require(:scholarship).permit(:name, ** Lots of params omitted for brevity **,
:notes, school: [:id])
end
end
The spec is where I can't seem to figure things out. For spec/controllers/scholarships_controller_spec.rb:
require 'rails_helper'
describe ScholarshipsController, type: :controller do
describe 'POST #create' do
context 'with valid attributes' do
before :each do
#school = create(:school)
#scholarship = #school.scholarships.create(FactoryGirl.build(:scholarship).attributes)
end
it 'receives :save' do
post :create, { scholarship: #scholarship.attributes, school: #school.id }
expect(#scholarship).to receive(:save)
end
end
end
end
When I run that test, I get the following error:
Failures:
1) ScholarshipsController POST #create with valid attributes receives :save
Failure/Error: post :create, scholarship: { attributes: #scholarship.attributes, school: #school.id } #school_id: #school.id, scholarship: #scholarship
ActionController::UrlGenerationError:
No route matches {:action=>"create", :controller=>"scholarships",
:scholarship=>{"id"=>"1", "name"=>"Dynamic Metrics Director Scholarship", *** Lots of parameters omitted for brevity ***
, "school_id"=>"2"}, :school=>"2"}
The parameters look correct to me. there's a set of attributes for the scholarship, and for the school. But the routing isn't working. I've tried a dozen different ways to try and get this to work. Heartened that I'm apparently passing a (more or less correct) parameters hash, but can't figure out quite where I'm going wrong.
****** EDIT ******
Updated in response to an answer posted below.
Changed the syntax of the spec as suggested by Srdjan:
it 'receives :save' do
post :create, "schools/#{#school.id}/scholarships", { scholarship: #scholarship.attributes, school_id: #school.id }
expect(#scholarship).to receive(:save)
end
This changes the error message. I assume that indicates that the parameters are being passed correctly, since it's no longer throwing an error related to routes/params..? Error message is:
1) ScholarshipsController POST #create with valid attributes receives :save
Failure/Error: expect(#scholarship).to receive(:save)
(#<Scholarship:0x007fe293b02598>).save(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Just for good measure, here are the relevant routes, which I hadn't posted previously:
school_scholarships POST /schools/:school_id/scholarships(.:format) scholarships#create
new_school_scholarship GET /schools/:school_id/scholarships/new(.:format) scholarships#new
school_scholarship DELETE /schools/:school_id/scholarships/:id(.:format) scholarships#destroy
In your test, you're POST-ing to the wrong route. As setup in routes.rb, scholarship resources do not exist out of the context of a school resource.
In order to fix this, you have to answer a question: "Does it make sense for a user to access a scholarship record without having to specify a school?"
If the answer is yes, you can either copy the scholarships route and paste them outside of the schools resource block. This way, you can have access to scholarships without having to specify a school, but also with specifying a school.
If the answer to the question is no, then you need to fix your test as such:
it 'receives :save' do
post :create, "schools/#{#school.id}/scholarhips", { scholarship: #scholarship.attributes, school_id: #school.id }
expect(#scholarship).to receive(:save)
end
I am currently writing functional tests for all of my controllers. For every single one, I can't get the create action to work. I keep getting the following error when I run rake test:
ActionController::UrlGenerationError: No route matches {:action=>"create", :comment=>{:content=>"I'm a comment.", :product_id=>"685617403"}, :controller=>comments}
Here is the action I am trying to test:
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
#product =Product.find(params[:product_id])
#comment=Comment.create(params[:comment].permit(:content))
#comment.user_id= current_user.id
#comment.product_id= #product.id
if #comment.save
redirect_to product_path(#product)
else
render 'new'
end
end
end
Here is the route:
POST /products/:product_id/comments(.:format) comments#create
The following is my test:
def setup
#user=users(:chris)
#product=products(:banana)
end
test "should redirect destroy action if not signed in" do
post :create, comment: { content: "I'm a comment.", product_id:#product.id}
assert_redirected_to new_user_session_path
end
I can't figure out what I am doing wrong. I am fairly certain I am passing the correct params in.I've also tried it with and without a user_id: param and it still throws the same error. It works fine on the app, I've called params in the web console when making the request and it matches what I am passing. The only thing I am missing is the :id but I assumed that would be assigned when the comment was created in the test. Also there are no unique constraints which would prevent the created comment from being valid. My associations are in place and when it works on the app it saves to the database with the user_id and product_id. What am I missing?
I think you need to put product_id as its own first-level param too in order for the route to match up correctly. So try this instead:
post :create, product_id: #product.id, comment: { content: "I'm a comment.", product_id: #product.id }
Note that in your controller action, you reference params[:product_id] directly already, you don't reference params[:comment][:product_id]. Then, to reduce duplication, you can create the Comment as a child of that Product in your controller:
#comment = #product.comments.create(other params...)
Rails' routing errors can be extremely vague and unhelpful. 90% of the time the error boils down to some variation of this: a mismatched or misnamed ID, a nil value, etc.
I'm still fairly new to the rspec way. I've read many posts on a redirect_to but cannot seem to get passed this error. I'm trying to simulate the passing of form variable/value in my rspec test, but getting a routing error. I'm converting tests to rspec tests on an app that is live and working.
In my employees_micros_controller.rb, I have an update method that expects params[:employees_micro][:id]. My question is How do I simulate this in my rspec test?
Controller:
def update
#em = nil
# check for id from form
if params[:employees_micro][:id]
#em = EmployeesMicro.find_by_id(params[:employees_micro][:id])
end
....
end
Rspec Test: note: ### error line ###
# update and redirect
describe 'POST #update' do
it "updates employee_micro and redirect to employee_details" do
# #emp_micros set in before(each) above
#emp_micros.home_job = 123
# update method looks for params[:employees_micro][:id]
post :update, :employees_micro => { :id => #emp_micros } ### error line ###
expect(response).to redirect_to("employee_details_employee_path")
end
end
Error:
Failure/Error: post :update, :employees_micro => { :id => #emp_micros }
ActionController::RoutingError:
No route matches {:employees_micro=>{:id=>"11960"}, :controller=>"employees_micros", :action=>"update"}
Is my >> post :update line syntax correct?
I do not understand the routing error. I'm only trying to simulate passing a form variable to the update method from my test. If I remove everything after the post/put line, I still get the same error, so it is definitely on that line.
I've also tried using "put" in place of "post" in the test. It nets me the same error.
Thanks for any tips or advice.
The update route expects the :id param to be at the top level, so use:
post :update, :id => #emp_micros, :employees_micro => { <other attributes to update> }
routes.rb
get 'students/name_starts_with/:letter', to: 'students#name_starts_with'
get 'students/with_last_name/:last', to: 'students#with_last_name'
students_controller.rb
def name_starts_with
#students = Student.all.select{|s| s.first_name.start_with?(params[:letter]}
render json: #students.to_json
end
def with_last_name
#students = Student.all.select{|s| s.last_name == params[:last]}
render json: #students.to_json
end
students_controller_spec.rb
context '#name_starts_with' do
let!(:first_student){Student.create(first_name: 'John', last_name: 'Doe'}
it "starts with #{first_student.first_name.split('').first}" do
get :name_starts_with
expect(response.status).to eq(200)
expect(first_student.first_name.split('').first).to be('J')
end
end
context '#with_last_name' do
let!(:first_student){Student.create(first_name: 'John', last_name: 'Doe'}
it "has last name #{first_student.last_name}" do
get :with_last_name
expect(response.status).to eq(200)
expect(first_student.last_name).to be('Doe')
end
end
I have seeded bunch of student names. As far as I know both of these should be get routes/get requests. I am getting same error for both:-
Failure/Error: get :name_starts_with
ActionController::UrlGenerationError:
No route matches {:action=>"name_starts_with", :controller=>"students"}
# ./spec/controllers/students_controller_spec.rb:45:in `block (3 levels) in <top (required)>'
Should they be POST routes instead. Am I missing something. Am I doing whole thing wrong. Can somebody please help me solve this issue please.
I think this is route matching error. parameter letter is missing in this call.
Replace
get :name_starts_with
with
get :name_starts_with, letter: first_student.first_name.split('').first
I think this will fix your error.
I know this sounds stupid, but is your StudentsController inheriting from ApplicationController? (ApplicationController inherits from ActionController::Base
...and I'm not sure if you need the #students.to_json if you already declared render :json in your controller. just put render json: #students
Following is my rails controller:
class MyController < ApplicationController
def index
#client = (current_company.clients.size || 0) >= current_company.subscription.clients # it returns true or false
begin
#obj = Class.all
respond_to do |format|
format.html # index.html.erb
end
rescue
end
end
end
Following is my rspec code under (spec/controller):
require 'spec_helper'
describe MyController do
describe "GET index" do
it "populates an array of data" do
current_company = mock_model(CompaniesUser)
clients = mock_model(Client)
get :index
.
.
end
end
end
After execution it provide me following error:
Failures:
1) MyController GET index populates an array of clients
Failure/Error: get :index
Double "Company_1" received unexpected message :clients with (no args)
# ./app/controllers/my_controller.rb:20:in `index'
# ./spec/controllers/my_controller_spec.rb:28:in `block (3 levels) in <top (required)>'
So how to do this association about current_compnay.clients.size in rspec controller? It provides an error due to not getting value current_company.clients.size in controller's index method from spec.
Not sure if I understand your question correctly. Are you looking for something like this?
it "populates an array of data" do
controller.stub(:current_company) {
mock_model(CompaniesUser, clients: [mock_model(Client)])
}
get :index
# ...
Some changes after your comment:
let(:client) { mock_model(Client, :id => 1)}
let(:company) { mock_model(Company, :id => 1, :clients => [client])}
before { controller.stub(:current_company).and_return(company) }
it "populates an array of data" do
get :index
# ...
disclaimer: please do not swallow errors!
what is that begin rescue end part for? please go ahead and remove that. it hides any error that occurs when rendering a template!
what is that #obj = Class.all is that pseudo code? if you add pseudo code, make a note about that!
if you have such complex logic in your controller, it would be a good idea to move it into a method of that class. so (current_company.clients.size || 0) >= current_company.subscription.clients might be refactored to a call of current_company.has_not_enough_clients or whatever your business logic should name it.
then go ahead and stub that method or use a test-double for only that specific model.
Problem resolved as follow:
at start of controller spec:
let(:current_company) {mock_model(CompanyUser, :id => 1, clients: [mock_model(Client)])}
now you can access it as "current_company.clients.size" gives "1"