rspec controller put update test not working - ruby-on-rails

I am stuck on a rspec test that is testing a update method in my controller. I am getting a routing error. Hoping someone smarter than I can help. I have googled and read many post on this site, but none have turned on the light-bulb in my head.
Note: this works when finding and updating a record via the browser. Just cannot figure out the test for it.
My Route for this controller update method:
PUT /admins/project_codes/:id(.:format) admins/project_codes#update
My controller update method:
def update
#project_code = ProjectCode.find(params[:project_code][:id])
if #project_code.update_attributes(params[:project_code])
redirect_to(:action => :show, :id => params[:project_code][:id])
else
redirect_to(:action => :edit, :id => params[:project_code][:id], :notice => "Your record for #{params[:project_code][:code]} could not be updated at this time.")
end
end
The edit form is using name="project_code[...]" for fields: i.e:
<input id="project_code_code" name="project_code[code]" size="10" type="text" value="F-UZBEKIST" /
So, in my test, I need create a record, then update a field and pass it to my update method. I'm trying to create the params[:project_code] to pass to the method.
spec:
describe "PUT #update" do
it "updates with valid name change" do
code = FactoryGirl.create(:project_code)
code.name = "new-name"
put :update, :project_code => code.attributes
...
end
end
My error:
1) Admins::ProjectCodesController PUT #update updates with valid name change
Failure/Error: put 'update', :project_code => code.attributes
ActionController::RoutingError:
No route matches {:project_code=>{"id"=>"10375", "code"=>"ABCDEFG", "name"=>"new-name", "update_by"=>"jdc44", "created_at"=>"2015-10-07 13:22:31 -0400", "updated_at"=>"2015-10-07 13:22:31 -0400"}, :controller=>"admins/project_codes", :action=>"update"}
Any help would be appreciated.

It seems your PUT /admins/project_codes/:id(.:format) requires an :id parameter in the url, try sending the following:
put :update, :id => code.id, :project_code => code.attributes

Related

Pass Form Vars in Rspec to Update Method

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> }

Testing Destroy Action in Rails API Controller

I'm building a RESTful JSON API with Rails 4.0 and using mongoid as my database. I'm testing the destroy action for one of my controllers using RSpec. However, when I run my test I receive the ActionController::UrlGenerationError: as Rails is unable to find a corresponding route. The weird thing is my destroy action is clearly defined in my routes.rb file and my other controller actions within the same resource work fine.
describe "DELETE 'delete credit cards'" do
before do
#credit = CreditCard.new(cc_last4: Random.rand(1234) )
end
it "should return a successful json response" do
params = {comicbook_uid: #user.comicbook_uid.to_s ,
notebook_token: #user.comicbook_token,
cc_id: #credit.id, format: :json }
delete :destroy, params
body_hash = JSON.parse response.body
expect(body_hash["success"]).to eql true
end
The error that gets generated in my terminal is this:
Failure/Error: delete :destroy, params
ActionController::UrlGenerationError:
No route matches {:comicbook_uid=>"52decf924761625bdf000000", :comicbook_token=>"sfsakjhfk",
:cc_id=>BSON::ObjectId('52decf924761625bdf020000'),
:format=>:json, :controller=>"credit_cards",
:action=>"destroy"}
My routes for the credit card resource look like this:
resources :credit_cards, only: [:create, :index, :destroy],
:defaults => { :format => 'json'}
Thank you in advance and hope can help me out soon!
You aren't passing an id attribute, which is the only param you should actually need. Moreover, since you are creating your #credit object using CreditCard.new(...) it won't actually have an id until you persist it to the db.
Replace:
it "should return a successful json response" do
params = {comicbook_uid: #user.comicbook_uid.to_s ,
notebook_token: #user.comicbook_token,
cc_id: #credit.id, format: :json }
with:
it "should return a successful json response" do
params = {id: #credit.id, format: :json }
and make sure to persist the CreditCard (or set mock expectations for it to be destroyed).

In Rails Controller testing, is there a way to pass query (non-routing) parameters?

I'm writing controller tests in Rails and RSpec, and it seems from reading the source code of ActionController::TestCase that it's not possible to pass arbitrary query parameters to the controller -- only routing parameters.
To work around this limitation, I am currently using with_routing:
with_routing do |routes|
# this nonsense is necessary because
# Rails controller testing does not
# pass on query params, only routing params
routes.draw do
get '/users/confirmation/:confirmation_token' => 'user_confirmations#show'
root :to => 'root#index'
end
get :show, 'confirmation_token' => CONFIRMATION_TOKEN
end
As you may be able to guess, I am testing a custom Confirmations controller for Devise. This means I am jacking into an existing API and do not have the option to change how the real mapping in config/routes.rb is done.
Is there a neater way to do this? A supported way for get to pass query parameters?
EDIT: There is something else going on. I created a minimal example in https://github.com/clacke/so_13866283 :
spec/controllers/receive_query_param_controller_spec.rb
describe ReceiveQueryParamController do
describe '#please' do
it 'receives query param, sets #my_param' do
get :please, :my_param => 'test_value'
assigns(:my_param).should eq 'test_value'
end
end
end
app/controllers/receive_query_param_controller.rb
class ReceiveQueryParamController < ApplicationController
def please
#my_param = params[:my_param]
end
end
config/routes.rb
So13866283::Application.routes.draw do
get '/receive_query_param/please' => 'receive_query_param#please'
end
This test passes, so I suppose it is Devise that does something funky with the routing.
EDIT:
Pinned down where in Devise routes are defined, and updated my example app to match it.
So13866283::Application.routes.draw do
resource :receive_query_param, :only => [:show],
:controller => "receive_query_param"
end
... and spec and controller updated accordingly to use #show. The test still passes, i.e. params[:my_param] is populated by get :show, :my_param => 'blah'. So, still a mystery why this does not happen in my real app.
Controller tests don't route. You are unit-testing the controller--routing is outside its scope.
A typical controller spec example tests an action:
describe MyController do
it "is successful" do
get :index
response.status.should == 200
end
end
You set up the test context by passing parameters to get, e.g.:
get :show, :id => 1
You can pass query parameters in that hash.
If you do want to test routing, you can write routing specs, or request (integration) specs.
Are you sure there isn't something else going on? I have a Rails 3.0.x project and am passing parameters.. well.. this is a post.. maybe it's different for get, but that seems odd..
before { post :contact_us, :contact_us => {:email => 'joe#example.com',
:category => 'Category', :subject => 'Subject', :message => 'Message'} }
The above is definitely being used in my controller in the params object.
I am doing this now:
#request.env['QUERY_STRING'] = "confirmation_token=" # otherwise it's ignored
get :show, :confirmation_token => CONFIRMATION_TOKEN
... but it looks hacky.
If someone could show me a neat and official way to do this, I would be delighted. Judging from what I've seen in the source code of #get and everything it calls, there doesn't seem to be any other way, but I'm hoping I overlooked something.

Test Ruby-on-Rails controller with RSpec and different route name

I have a Rails model named Xpmodule with a corresponding controller XpmoduleController.
class XpmoduleController < ApplicationController
def index
#xpmodule = Xpmodule.find(params[:module_id])
end
def subscribe
flash[:notice] = "You are now subscribed to #{params[:subscription][:title]}"
redirect_to :action => :index
end
end
The original intent was to name the model Module which for obvious reasons doesn't work. However I still want to have the URLs look like /module/4711/ therefore I added this to my routes.rb:
map.connect '/module/:module_id', :controller => 'xpmodule', :action => 'index'
map.connect '/module/:module_id/subscribe', :controller => 'xpmodule',
:action => 'subscribe'
Now I want to test this controller with Rspec:
describe XpmoduleController do
fixtures :xpmodules
context "index" do
it "should assign the current xpmodule" do
xpm = mock_model(Xpmodule)
Xpmodule.should_receive(:find).and_return(xpm)
get "index"
assigns[:xpmodule].should be_an_instance_of(Xpmodule)
end
end
end
for which I get No route matches {:action=>"index", :controller=>"xpmodule"}. Which of course is sort-of right, but I don't want to add this route just for testing purposes. Is there a way to tell Rspec to call a different URL in get?
As far as I can tell you're testing controller action and not routing to that action. These are two different things!
Try this for starters:
it "should map xpmodules controller to /modules url" do
route_for(:controller => "xpmodule", :action => "index").should == "/modules"
end
Apply for other actions as well. If you want to do a reverse routing (from url to controller/action) then do this:
it "should route /modules url to xpmodules controller and index action" do
params_from(:get, "/modules").should == {:controller => "xpmodules", :action => "index"}
end
Head, meet wall, Wall, meet head. bang.
Not getting an answer on SO is a sure sign that I should try harder. Therefore I added the /xpmodule route explicitly to the routes.rb. Just to notice that the tests are still failing. To cut a long story short:
it "should assign the current xpmodule" do
xpm = mock_model(Xpmodule)
Xpmodule.should_receive(:find).and_return(xpm)
get "index", :module_id => 1
assigns[:xpmodule].should be_an_instance_of(Xpmodule)
end
is the solution.

How to make fixtures updateable in Rails' tests?

Below I listed some code from simple Rails application. The test listed below fails in last line, because the updated_at field of the post is not changed within the update action of PostController in this test. Why?
This behaviour seems to me a little strange, because standard timestamps are included in Post model, live testing on local server shows that this field is actually updated after returning from update action and first assertion is fulfilled thus it shows the update action went ok.
How can I make fixtures updateable in above meaning?
# app/controllers/post_controller.rb
def update
#post = Post.find(params[:id])
if #post.update_attributes(params[:post])
redirect_to #post # Update went ok!
else
render :action => "edit"
end
end
# test/functional/post_controller_test.rb
test "should update post" do
before = Time.now
put :update, :id => posts(:one).id, :post => { :content => "anothercontent" }
after = Time.now
assert_redirected_to post_path(posts(:one).id) # ok
assert posts(:one).updated_at.between?(before, after), "Not updated!?" # failed
end
# test/fixtures/posts.yml
one:
content: First post
posts(:one)
That means "fetch the fixture named ":one" in posts.yml. That's never going to change during a test, barring some extremely weird and destructive code that has no place in sane tests.
What you want to do is check the object that the controller is assigning.
post = assigns(:post)
assert post.updated_at.between?(before, after)
On a side note if you were using shoulda (http://www.thoughtbot.com/projects/shoulda/) it would look like this:
context "on PUT to :update" do
setup do
#start_time = Time.now
#post = posts(:one)
put :update, :id => #post.id, :post => { :content => "anothercontent" }
end
should_assign_to :post
should "update the time" do
#post.updated_at.between?(#start_time, Time.now)
end
end
Shoulda is awesome.

Resources