[rspec-users] lots of nil problems!

Phillip Koebbe phillipkoebbe at gmail.com
Sat Mar 20 09:53:39 EDT 2010


On 2010-03-20 7:02 AM, Patrick J. Collins wrote:
> On Sat, 20 Mar 2010, Phillip Koebbe wrote:
>
>    
>> Welcome to RSpec, Patrick. For some of us, it's pretty rocky at first. I
>>      
> Thank you Phillip for your great explanation...
>
> After reading what you wrote, I have a few questions:
>
> 1.  From what I understand of what you wrote, stub_model makes a fake object,
> with accessor methods for the AR columns, and they return nil.
>
> So what happens behind the scenes with mock_model?  What is the difference?
>
>    

Looking at http://rspec.rubyforge.org/rspec-rails/1.3.2/, I can't see 
any difference between mock_model and stub_model. David?

>
> 2.  I sort of asked this earlier, but didn't really get addressed---  What is the benefit to doing stubbing
> vs. using a factory or fixture?
>
>
>    

I passed on this question since I am not very familiar with projects 
like factory_girl or machinist. I'd like to look into them more at some 
point, but just don't have the time right now. As for fixtures, I can't 
stand them. We used fixtures on that project I mentioned earlier. At 
first, they were great. But as the project grew, it was a nightmare 
keeping them working right. I *much* prefer mocking and stubbing.

> 3.  In your code, you wrote:
>
>    
>> context 'when record is not found' do
>>    SomeClass.should_receive(:find).and_return(nil)
>>      
> I just wondered about this, because-- correct me if I am wrong, but a record not found would not return
> nil-- it returns a record not found error, correct?  Or does an error actually equate to nil?  I know in a
> lot of my controllers to avoid record not found errors, I do .find_by_id instead of .find, because that
> will return nil if it's not found.
>    

Right. I goofed there. It should have been .find_by_id. The point that I 
was hoping to make, though, is that you can create the environment 
needed to test what your controller does when certain conditions are 
met. Consider the example of wanting to redirect to an error page if a 
resource is not found:

context 'resource is found' do
   before :each do
     @some_object = stub_model(SomeClass)
     SomeClass.should_receive(:find_by_id).and_return(@some_object)
     get :show, :id => 1
   end

   it 'should render the show page' do
     # not sure if my syntax is correct here...i use remarkable which 
has a macro for it
     response.should render(:show)
   end
end

context 'resource is not found' do
   before :each do
     SomeClass.should_receive(:find_by_id).and_return(nil)
     get :show, :id => 1
   end

   it 'should redirect to error page' do
     # not sure if my syntax is correct here...i use remarkable which 
has a macro for redirects
     response.should redirect_to error_path
   end
end

You are validating that the controller code works correctly. It would 
look like:

def show
   @some_object = SomeClass.find_by_id(params[:id])
   redirect_to error_path and return unless @some_object
end

>
>
> 4.  should_receive...  So, if you have a method that is calling other class and instance methods, isn't it
> a given that those calls are important and that they should be expected?  In other words, I am finding
> myself asking the same question again-- Why use stub instead of should_receive?  When would you actually
> not care whether a method gets called or not if you are testing something.  I mean, I would assume if I am
> testing something, I want to test everything that happens-- so any real code in my app that deals with
> class/instance methods, those seem like they would be important and therefore those calls should be
> expected.  ?
>
>    
Think about testing in isolation, but now isolating specific pieces of 
functionality in your controller. If I have already tested one piece 
with expectations, when I move on to another piece, I can just stub them 
and focus on the code at hand.

This example isn't necessarily controller-specific, but maybe it will 
help see the difference. Suppose you have a method that performs some 
calculation. At one point in your testing, you should verify that the 
method gets called when you expect it to. That's when you would use 
.should_receive. At this point, you don't really care what the result 
is, just that your application code is invoking the calculation at the 
right time. Now, when you are validating what the application does *in 
response* to the result of the calculation, that's when you would just 
stub it since you don't really care about the calling of it, but what it 
returns. So if you want to do one thing when the result is A and another 
when it is B, you would stub it .and_return(A) in one case and 
.and_return(B) in the other.

>
> 5.  Since I learn really well from example, I just wrote up some random and strange code, which does a
> handful of things, and I would love it if you could go through and spec it-- and explain each step of the
> way, what you're doing and why.  What is necessary to test, and what is not, and of course also-- why.
>
> def index
>     type = params[:foo]
> 	value = params[:bar]
>
> 	@foo = type&&  value ? Foo.send("find_by_#{type}", value) : Foo.all
>
> 	@baz = /.*lower/.match(params[:baz]) ? @foo.lowered_name : @foo.upper_name
>
> 	flash[:notice] = "Something with #{@foo.numbers}" unless params[:bool].to_i.zero?
> 	
> 	respond_to do |format|
> 		format.html { render 'foo_listing' }
> 		format.js { render :json =>  { :foo =>  @foo }.to_json
> 		format.pdf { render :inline =>  @foo.pdf }
> 	end
> end
>
> #
> class Foo<  AR::base
>
> def lowered_name
>    name.downcase
> end
>
> def upper_name
>    name.upcase
> end
>
> def numbers
>    rand(id)
> end
>
> def pdf
>    # return some prawn pdf object
> end
>
> ...
>
> If you could do that for me, it would be incredibly awesome...
>
>    

I would be glad to help in this way, but it will have to be later today. 
And instead of using completely made-up code, I'll use some of my 
real-world code and tests as an example. I'll gist them later, unless 
one of the gurus comes along and provides enlightenment for you!

Peace,
Phillip



More information about the rspec-users mailing list