Rspec Test controller render partial with locals - ruby-on-rails

I have controller action like
def get_status
status_name = current_user.status
status_updated_time = current_user.updated_at
render :partial => 'show_status', :locals => {status_name: status_name, status_updated_time: status_updated_time}
end
here I am planning to test local variable values which are passing via render partial. i.e
status_name, status_updated_time.
Could you please let me know how to write rspecs for render partial with locals in controller.

I would move variable logic into a separate method:
def get_status
render partial: 'show_status', locals: get_status_from(current_user)
end
protected
def get_status_from(user)
{ status_name: user.status, status_updated_time: user.updated_at }
end
and test that method instead.

I would say that to test the controller, what you're after is a basic feature/integration spec wherein you can simply look for the content held by your partial.
feature 'SomeController' do
background do
# setup data
# and anything else you need for authentication, etc. as your site dictates
end
scenario 'viewing the get status page' do
visit some_controller_get_status_path
expect(page).to have_text('WHATEVER IS IN THE LOCAL VAR')
end
end
I prefer to use feature specs over controller specs as I seek (but often fail!) to keep my controllers so simple that there is not really much to test in them. With feature specs, I feel like I'm getting more from the test in terms of how my app works, etc.
EDIT: sorry ... hit enter too early :).
For a controller, you could directly test the var value along the lines of:
describe "Your Controller", :type => :controller do
describe "GET get_stuff" do
it "assigns a value to status_name" do
get :get_status
expect(assigns(:status_name)).to eq(['VALUE'])
end
end
end
That may not be 100% spot-on for a controller spec (again, I don't use them a lot) but I think it should get you on your way should you go controller spec over feature/integration spec.

you could do something like
it "should render correct partial for get_status" do
controller.should_receive(:render).with({
:partial => '_show_status', #here you will have to give the full path like <controller_name>/_show_status
:locals => {status_name: <name>, status_update_time: <time>}
})
get 'get_status'
end

Related

RSpec controller testing: missing template on create

I have an interesting situation. I am testing the following simple create action:
# will only be accessed via Ajax
def create
click = Click.new(params[:click])
click.save # don't really care whether its success or failure
end
Then I have the following very simple controller spec:
require 'spec_helper'
describe ClicksController, "creating a click" do
it "should create a click for event" do
xhr :post, :create, :click => {:event_id => 1}
# more test to come...
end
end
Seems trivial, yet I get the following:
Missing template clicks/create
Any tips would be appreciated.
Add to the controller action:
render :nothing => true
This one will automatically create the appropriate server's respone. More here
You will get this error if your controller renders only JSON or XML, yet you don't specify a format in the spec; your request then defaults to unsupported HTML. In that case, simply specify the supported format when you invoke the controller method from your spec. For example, change this:
post :create, registration: #user_hash
to this:
post :create, registration: #user_hash, format: :json
If you do not render anything in a controller action, rails will attempt to default to rendering a template (in this case clicks/create). I'd suggest rendering back at least a success message like so:
render :json => {:success => true}
Building on megas's answer, if you're looking to test a controller action that's only accessed via a UJS link and only has a .js.erb template, I'd put this in the controller to avoid breaking your UJS functionality:
respond_to do |f|
f.html { render nothing: true } # prevents rendering a nonexistent template file
f.js # still renders the JavaScript template
end
This will enable you to call the controller action by simply calling ActionController::TestCase::Behavior's get/post/put/delete methods instead of needing to call xhr, because it will successfully call the method, render nothing, and continue on, while leaving your UJS behavior intact.

How to validate locals of render template in rspec

I wonder how to validate the locals passed to render template in controller
Controller:
def lelf_panel
# ...
if some_condition
locals_hash = some_very_long_hash_A
else
locals_hash = some_very_long_hash_B
end
render :partial => "left_panel", :layout => false, :locals => locals_hash
end
Current Spec:
it 'should render correct template for lelf_panel' do
# ...
get 'left_panel'
response.should render_template('system/_left_panel')
end
Now I need to finish Rcov for this controller so I need to add/modify spec to cover both 'some_condition' results. and I want to validate 'lelf_panel' locals passed to render, as if I only validate the render_template, partial page rendered for both result are the same.
I check the 'render_template' in rspec docs in
http://rubydoc.info/gems/rspec-rails/2.8.1/RSpec/Rails/Matchers/RenderTemplate:render_template
it only provide and 2nd params for message, so how can I test the locals passed to render?
Instead of using the render_template matcher, you can use an expectation on the controller object.
it 'should render correct template for lefl_panel' do
# ...
allow(controller).to receive(:render).with no_args
expect(controller).to receive(:render).with({
:partial => 'system/_left_panel',
:layout => false,
:locals => some_very_long_hash_A
})
get 'left_panel'
end
Same as #ryan-ahearn 's answer with suggestions from #user2490003 's comment - but all put into something more flexible and for RSpec 3.
# Safe to set globally, since actions can either render or redirect once or fail anyway
before do
allow(controller).to receive(:render).and_call_original
end
describe "get left panel" do
before do
# other setup
get 'left_panel'
end
it 'should render correct template for lelf_panel' do
# Sadly, render_template is primitive (no hash_including, no block with args, etc.)
expect(subject).to render_template('system/_left_panel')
end
it 'should render with correct local value' do
expect(controller).to have_received(:render) do |options|
expect(options[:locals][:key_from_very_long_hash]).to eq('value_of_key_from_very_long_hash')
end
end
end
as far as I know, there is no way to directly examine the locals for a template in the way you're describing.
You could change locals_hash to #locals_hash and then examine the results through assigns( :locals_hash).
Or, you could use selectors on the resulting HTML and check that some indicative content is there -- for instance, if locals_hash affects the title of the page, check that the resulting HTML page title is what you expect.

Is it better to create 2 actions, 1 for get and 1 for post, or can you just merge the 2?

Is it possible to have a controller action do both the GET and POST?
i.e. the GET shows a form, and the POST takes the forms values and saves to the db.
As already mentioned it is possible, but I feel it is bad style. Showing a form and saving something are different actions and your code should reflect that.
If you just want to access both action via the same url you can just set up your routes accordingly. This is done differently depending on whether you use Rails 2 or Rails 3.
Yes it's possible. You just need check the method to call you action
def show
if request.post?
render :text => "it's a post"
elsif request.get?
render :text => "it's a get"
else
render :text => "it's another method"
end
end

How do I force a namespace for a helper from within a spec

I am trying to test the output of a view helper that resides within a namespace. The original helper is located under app/helpers/admin/events_helper.rb. The test is at spec/helpers/admin/events_helper_spec.rb and looks like this (simplified):
require File.dirname(__FILE__) + '/../../spec_helper'
describe Admin::EventsHelper do
fixtures :events, :users
before(:each) do
#event = events(:one)
#user = users(:one)
end
it "should include link to admin page for user" do
html = helper.event_message(#event)
html.should have_selector("a", :href => admin_user_path(#user))
end
end
The helper, ridiculously simplified, looks like this:
module Admin::EventsHelper
def event_message(event)
link_to(
event.message,
:controller => 'users', :action => 'show', :id => event.user.id)
end
end
When the event_message method is called from a controller within the Admin namespace, it renders the link as '/admin/users/:id' as intended. However, called from the spec, it renders as '/users/:id', making the test fail.
How do I specify the correct namespace to use for the helper within the spec?
Thanks!
I think the problem stems from the way Rspec (and the Rails test framework) handles controllers. For complex reasons (OK, reasons I don't understand), you don't get a real ActionController when testing, instead you get an instance of ActionView::TestCase::TestController. When using namespaces, the test controller in this case is not correctly inferring the actual controller path, so it guesses "/users", which is wrong.
Long story short, while there is probably a better way to do it, you can try stubbing out the controller method that gets called by url_for to generate the link:
it "should include link to admin page for user" do
controller.stub(:url_options).and_return(:host=>"test.host", :protocol=>"http://", :_path_segments=>{:controller=>"admin/users", :action=>"show"}, :script_name=>"")
html = helper.event_message(#event)
html.should have_selector("a", :href => admin_user_path(#user))
end
If i get your question correct, you are asking about specifying the controller namespace which is failing in spec right?
If you UsersController is within Admin namespace, then you should be doing this:
link_to(event.message, :controller => 'admin/users', :action => 'show', :id => event.user.id) in your helper method.
Note the value for controller key is admin/users

How to properly test this controller action with Shoulda?

I have the following controller action and test. I'm new to testing with Shoulda and I know there are areas of my controller that I can test further. For example, my flash messages as well as verifying the renders.
So my question is, how would I properly test this controller action in Shoulda?
My controller action (names have been changed to protect the innocent):
def my_action
return redirect_to(root_url) if #site.nil?
#owner = current_site.owner
if request.post?
if params[:password].blank? || params[:email].blank?
flash[:error] = "You must fill in both the e-mail and password fields"
render :action => "my_action"
else
if #owner.authenticated?(params[:password])
#owner.login = params[:email]
#owner.save!
#owner.do_some_method
flash[:success] = "Success."
render :action => "my_action"
else
flash[:error] = "Incorrect password"
render :action => "my_action"
end
end
end
end
My test:
context "on POST to :my_action" do
setup do
Owner.any_instance().expects(:do_some_method)
post :my_action, :email => 'foo#bar.com', :password => 'test'
end
should_assign_to :owner
should "Change name and verify password and resend activation key" do
assert_equal true, assigns(:owner).authenticated?('test')
assert_equal 'foo#bar.com', assigns(:owner).login
end
should_respond_with :success
end
Right now, it appears that you're testing functionality specific to the model inside your controller, that should be in a unit test.
I would advise re-factoring your controller to include the required logic for updating the Owner's email inside the Owner model. By doing that, you should be able to simplify the controller down to a simple if update; else; end type statement and greatly simplify the controller test. Once you've moved the logic into the model, you can use built in Rails validations.
A couple of other things to consider:
Redirecting after your POST action completes, prevents the user from double-posting by accident (most browsers will complain when the user attempts it).
Move the checking for #site and also the assignment to #owner to before_filters if this is done more than once inside the controller.
You can avoid having to check if request.post? with either verify or creating a route in `config/routes.rb'.
References:
Skinny Controller, Fat Model
RailsConf Recap: Skinny Controllers
ActionController::Filters::ClassMethods
ActionController::Verification::ClassMethods

Resources