rspec 'allow' stub isn't setting variables - ruby-on-rails

I have a controller function:
def update
#simulation = Simulation.find(params[:id])
#simulation.next
puts "--"
puts #simulation.dirty?
puts #simulation.save
if (#simulation.save && #simulation.dirty?)
render :partial => 'show', :object => #simulation
end
end
And an rspec test:
it "should render a partial when the record is dirty" do
allow(#simulation).to receive('dirty?') { true }
put :update, :id => #simulation.id, :format => 'js'
expect(response).to render_template( :partial => 'show' )
end
The test is failing to render the view because the if check isn't passing because it won't return true for #simulation#dirty? even though the function is stubbed. I can see this because of the puts in the controller. Any ideas why it's not working?

The instance variable #simulation you are stubbing does not belong to the controller instance, but to the rspec test case class instance. Try #simulation.dirty? within it block of rspec, after the allow method call. I guess it returns true. The #simulation in the controller is not stubbed, though. They are two different objects.
If you want to stub #simulation in the controller's update method, you should stub all the instance of the Simulation class. Try using allow_any_instance_of instead of allow(#simulation).
allow_any_instance_of(Simulation).to receive(:dirty?).and_return(true)
https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class

Related

Rspec Test controller render partial with locals

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

view.stub in rails partial rspec gives 'undefined method view_context'

using Rails 3.2.11
I have a couple of view rspec tests where I need to stub the 'current_user' call.
I've used this successfully in a regular view test like so:
require 'spec_helper'
describe "projects/_my_project.html.erb" do
before(:each) do
#client = FactoryGirl.create(:user)
view.stub(:current_user) { #client }
end
describe "new proposals notice" do
it "does not display new_propsals if in state posted and clicked after last submitted" do
#my_project = FactoryGirl.build(:project, status: "posted", last_proposals_click: "2012-02-02 14:01:00", last_proposal_submitted: "2012-02-02 14:00:00")
render :partial => "/projects/my_project", :locals => { :my_project => #my_project }
rendered.should_not have_content "You received new proposals"
end
end
end
Current_user is defined by Devise in controllers/helpers.rb (in the gem). I use it all over the place as current_user (as a method, not instance) in the view or controller.
The problem seems to be around view in view.stub being nil here, is there another object that is used in case of partials?? I simply don't understand why this works perfect in a regular view and not in a partial.
I get:
Failure/Error: view.stub(:current_user) { #client }
NoMethodError:
undefined method `view_context' for nil:NilClass
Here is the line from the view where current_user is used for completeness:
<% if my_project.user_id == current_user.id %>
Does anyone know how I can get it to stub current_user successfully, I'm at a loss here...
thanks
Turns out that moving view.stub(:current_user) { #client } into each 'it' block solved the problem; it does not seem to work if it is in the 'before:each/all' block.

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.

Despite a controller error my tests still pass, while visiting the edit url correctly shows an error. Why is this so?

I have a Rails controller where I accidentally defined the 'edit' method inside the 'create' method.
My Controller with the error:
class UsersController < ApplicationController
...
def create
#user = User.new(params[:user])
...
def edit
#user = User.find(params[:id])
#title = "Edit user"
#check = "BORK" # something I added for testing the rendered output
end
end
end
An example test;
it "should have the right title" do
get :edit, :id => #user
response.should have_selector('title', :content => 'Edit user')
end
So when I run the tests (I use rspec) and output the response.body, the User edit.html.erb template is rendered correctly; all the instance variables are visible. So all the tests pass.
Visiting the 'edit' URL correctly shows an error; the template uses #user instance variable, and it's not set correctly. Of course correcting the controller fixes the error.
I don't understand why the tests pass at all and why, in the test, all the instance variable values are visible?
My instinct suggests this is a scope problem? Something about #user being an instance variable, and that in the tests it's set within the scope of the test, but in my controller it's within the scope of the inner 'edit' method? But how does the test even find the 'edit' method? In what scope does that inner 'edit' method exist?
You should realise that the def construct is as much executable code as an if statement. It's not invalid to put it inside another method, but it won't be run until the outer method is called:
>> class Foo
>> def foo
>> def bar
>> end
>> end
>> end
=> nil
>> Foo.instance_methods(false)
=> ["foo"]
>> Foo.new.foo
=> nil
>> Foo.instance_methods(false)
=> ["foo", "bar"]
The reason this was erroring in your browser was because Rails reloads all (most) of your classes each request. So, even if you had visited the create action - which would cause the edit method to be defined - the following request would have unloaded it again.
However in the test environment, if an earlier test had called the create action then that would have defined the edit action for future tests. You would see a different result if your tests were run in a different order (which in itself makes it a bad idea to rely on this).
Generally of course this isn't what you want at all, so just clear it up and move along :)

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