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

_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to