Issue with application controller spec

I’m having some trouble trying to spec some methods in my Rails
application controller.

I’m new to rspec converting over from straight test::unit.

Here is the method in my application.rb controller that I’m trying to
spec

def render_403
logger.debug “Returned 403: #{request.request_uri}”
render :file => “#{RAILS_ROOT}/public/403.html”, :status => ‘403’
end

Here is my current spec

describe “handling render 403” do

 before(:each) do

controller.request.should_receive(:request_uri).and_return(‘response
request_uri’)
end

 it "should render the 403 error page" do
   controller.render_403
   response.should render_file("#{RAILS_ROOT}/public/403.html")
 end

 it "should send a message to logger" do
   @log_stream = StringIO.new
   controller.stub!(:logger).and_return(Logger.new(@log_stream))
   controller.render_403
   @log_stream.string.should match(/Returned 403:/)
 end

end

Both specifications fail with the following error:

You have a nil object when you didn’t expect it!
The error occurred while evaluating nil.template

/app/controllers/application.rb:25 :in `render_403’
/spec/controllers/application_controller_spec.rb:34

The errors is being raised on the render :file line of the render_403
method and I have no idea what it is referring too. If I comment out
that line, then my logger spec test passes fine but I need to
obviously test that the render happens and I can’t figure out this
error and google has provided no help.

On Fri, Sep 5, 2008 at 10:48 PM, Scott T.
[email protected] wrote:

def render_403
before(:each) do
response.should render_file(“#{RAILS_ROOT}/public/403.html”)

would pass

but

an_object.a_method_call
an_object.should_receive(:a_method_call)

would fail

“should render_file” is not a mock expectation, it’s a post-action,
more state-based expectation, so Craig’s got this bit right in this
case.

  1. Usually, you make a request in controller specs, like “get :render_404”.
    See the controller docs on the rspec site.

I think this is the crux of the problem. Craig, you’d probably learn a
lot by running these code examples with the -b flag to get a full
backtrace.

Hope that helps,

It does :slight_smile:

HTH too,

David

On Sep 5, 2008, at 10:18 PM, Craig P Jolicoeur wrote:

render :file => “#{RAILS_ROOT}/public/403.html”, :status => ‘403’
controller.request.should_receive(:request_uri).and_return(‘response
request_uri’)
end

First off - should_receive is a test - why would you write a test in
the setup?

it “should render the 403 error page” do
controller.render_403
response.should render_file("#{RAILS_ROOT}/public/403.html")
end

Two issues with this test:

  1. Normally, if you are going to write a test checking that a method
    is called on an object (should_receive), you’d write that assertion
    before you perform any action. For example:

an_object.should_receive(:a_method_call)
an_object.a_method_call

would pass

but

an_object.a_method_call
an_object.should_receive(:a_method_call)

would fail

  1. Usually, you make a request in controller specs, like
    “get :render_404”. See the controller docs on the rspec site.

Hope that helps,

Scott

Thanks David.

I changed to using get :render_403 and it worked. Well, worked after
I added in those routes to my routes.rb file.

I dont have the default routes for /:controller/:action/:id so it was
failing and I dont want custom routes for these two methods because
they will be called from other controllers directly and don’t need a
named route.

What is the proper way to test application controller methods that get
called from other controllers? I will never manually go to /
application/render_403 but it seems I had to pretend this would happen
to spec it out properly.

  • Craig

First off - should_receive is a test - why would you write a test in
the setup?

Not sure what you mean by that. I’m not writing a test in the setup
routine.

Also, I’m not setting an expectation on a method call. should
render_file is in the right place.

On Sep 5, 11:48 pm, Scott T. [email protected]

On Sat, Sep 6, 2008 at 8:55 AM, Craig J. [email protected]
wrote:

First off - should_receive is a test - why would you write a test in
the setup?

Not sure what you mean by that. I’m not writing a test in the setup
routine.

should_receive is an expectation which could pass or fail. I think
that’s what Scott meant by writing a test in setup (before(:each)).
It’s something you generally want to avoid because it makes it more
difficult to understand failures if they’re not right in the actual
code examples.

Also, I’m not setting an expectation on a method call. should
render_file is in the right place.

You’re not now, but you could.

I think part of my problem, now that I think about it, is that I’m
trying to test the method instead of testing behavior. This
render_403 method should probably not be tested directly from the
application_controller_spec since it doesnt get actually called
their. I should test the render method from the controllers where the
behavior is actually expected since it makes more sense.

Thanks for all the help

On Sat, Sep 6, 2008 at 9:15 AM, Craig J. [email protected]
wrote:

What is the proper way to test application controller methods that get
called from other controllers?

The problem to solve here is the fact that Rails is providing a bunch
of setup for us when we go through an action, and the render method in
the controller relies on that setup.

There are (at least) two ways you could go here, neither of which is
perfect, but perhaps you’ll like them better than what you’re doing.

One would be to set a message expectation on render:

controller.should_receive(:render).with(:file =>
“#{RAILS_ROOT}/public/403.html”, :status => ‘403’)
controller.get_403

That’s a very implementation specific approach, and this is the sort
of thing that leads to endless debate over the costs/benefits of
mocking/stubbing. In this particular case, though, it’ll get you the
answer you’re looking for.

Another approach is to create a fake controller:

class FakeController < ApplicationController
def action_that_raises_a_403
get_403
end
end

and add routing for this controller only if ENV[‘RAILS_ENV’] == ‘test’
(in spec_helper or somewhere in the spec directory).

For that matter, you could just add routing for get_403 directly, but
because it itself is not really an action, but a helper, it might be
more clear to not call it directly, but only from an action.

HTH,
David