Welcome to RSpec, Patrick. For some of us, it's pretty rocky at first. I started using it a couple of years ago with models, and understood that well enough (I think). Then I came to controllers and I just couldn't wrap my mind around it. I gave up for quite some time. When I came back to testing, it was on a project that was using test::unit, but at least I was working with someone who had more experience than I did, and I was able to gain some understanding that I lacked earlier. But during the use of test::unit, I realized I missed RSpec, so when I moved on, I put RSpec back into my workflow. I've been trudging along for a few months now, and while I still have lots and lots to learn, I think I am actually using it somewhat correctly and productively. So while I definitely don't want to try to supersede Nick's or anyone else's contributions to your questions, I thought I would chime with what I've learned.

On 2010-03-20 2:28 AM, Patrick J. Collins wrote:
Hi Nick,

Thank you very much for your reply.  One thing I am finding incredibly
frustrating with Rspec, is that I don't even know what questions to ask--

Ask everything. In my life, I've learned that the only stupid question is the one you didn't ask. Most of the people in the RSpec community are *very* understanding, patient, and incredibly helpful. No one is going to bite your head off!

because I find the whole thing so confusing.  So forgive me, but I am going to
break down your code and ask specific questions to hopefully gain
understanding.

1.  Ok-- so in your example (which I greatly appreciate), you have this 
do_action
method..  Is that where I would put something like

get :create, :format =>  :pdf

?

...

2. Next in the before each block, you set @address..

Now, I have factory girl installed and have used it in cucumber, is that
something that could be used instead of setting @address to "123 some st" ?  Is
that common practice and acceptable to do?

3.  You set shipping_setting to mock the model Setting with
:address set to @address....

> From my understanding of what I've read--  Mocking a model,
simply means that it's a fake object, that acts like the real
thing------  So, when you do that mock_model, is it virtually
the same to have just done:
@shipping_setting = Setting.create(:address =>  @address)

?  That would be utilizing the test database, correct?  By
using mock_model, nothing is being stored, right?  It's more
just temporary data in memory... ?


No, you're not using the test database. That's the point of mocking: you want to break the dependency on another layer. When you test, you want to try to isolate what you are testing so you can specify how it behaves given certain criteria. For example, when you test a model, you don't want a controller involved. When you test a controller, you don't really want a model involved. Mocking and stubbing allow you to completely control all (or most) of the extenuating circumstances that your controller will find itself in. Then you can properly test something like "When the controller receives these params, it should do blah blah blah"

4.  You do .stub! ...........  This is where I get really
confused.  From the peepcode screencast that I have watched
several times, he explained that stubbing is the same as
mocking-- except there is no expectation of what it returns.


Stubbing is creating the "plumbing" that your application expects to exist to function properly. If you have a person object and you want to do something with the name attribute, you code will be expecting "person.name" to work. If you have a fake object @person and call @person.name, you will get an error unless you stub it like

@person.stub!(:name).and_return('John Doe')

That made sense when he said it, but in all the examples I've
seen with code (including yours), there is this .and_return at
the end..  Which as far as I can tell, is an expectation..  I
mean, you are asking it to return a specific thing-- that is
an expectation is it not?  Or does expectation really mean
something else in this context?


The .and_return() bit just says "for this stubbed method, return this value when it's called." Keep in mind that these objects you are creating are going to be interacting with your application code. So if your application code calls Person.new, it expects to receive back a person object. So you can do something like this in your specs:

@person = stub_model(Person)
Person.stub!(:new).and_return(@person)

Then you will be bypassing your Person model completely but still sending something back for your code to play with. Now, I've introduced another method in this mess, stub_model. This is very similar to stub, except that it goes ahead and and stubs out the attribute methods that are on your model. So if you have Person with name, age, and gender, the single call to stub_model also does this for you:

@person.stub!(:name)
@person.stub!(:age)
@person.stub!(:gender)

By default, the stubs created with stub_model will return nil. If you need to specify return values, do something like this:

@person = stub_model(Person, :name => 'John Doe', :age => 99, :gender => 'M')

That aside, if I try to guess what this Setting.stub! this is
doing-- it is pretending that you are actually looking for a
record, and getting it.....  but in this case, is that even
worth doing?  I mean, the shipping_setting record is really
only important in the sense that the record is needed to
provide a valid "from" address for a label..  But, if we're
dealing with a test database where a from address doesn't
exist-- then it seems kind of pointless..  To clarify, the
importance for me would be to have a test that actually looks
in my real database and makes sure there is a shipping from
address in there...  I have no idea if this paragraph will
make sense to you, but hopefully it will.

Here is where testing in isolation comes back into play. If you are testing a controller, you want to try to avoid the dependence on the database. Ideally, you want to use mocking and stubbing to create an artificial environment, that you completely control, to specify the behavior of your controller. Think about each situation you expect your controller to encounter. For each one of those situations, create the imaginary world that needs to exist, and then test that your controller behaves correctly.

For example, if your controller needs to do one thing if a particular record is found and something else if it is not found, you should have two sets of specifications. I usually do something like this:

context 'when records is found' do
  before :each do
    @record = stub_model(SomeClass)
    SomeClass.should_receive(:find).and_return(@record)
  end

  it 'should test something' do
    blah blah blah
  end
end

context 'when record is not found' do
  SomeClass.should_receive(:find).and_return(nil)

  it 'should test something else' do

  end
end

I've thrown in another method, .should_receive. This establishes an expectation that this method should be called during the execution of your code. If it does not get called, you will get a failure. Stub!, on the other hand, just creates the method *in case* it gets called. No error will be raised if it doesn't. So in the bogus code above, I am saying that I expect my controller to call SomeClass.find, and I want to test what it does when the find is successful and when it isn't. When it is successful, it will be returning something that the application will think is an instance of the class, and when it is not successful, it will be returning nil, which is what normally happens in AR. However, because I'm "mocking and stubbing", I'm bypassing AR completely.

That's all I have time for right now. I hope this has helped a little bit. I encourage you to take deep breaths often and just hang in there. Keep asking questions. Soon or later, the light will click and you'll have an "Aha!" moment.

Peace,
Phillip

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

Reply via email to