El 09/07/2010, a las 14:29, Frank J. Mattia escribió:

>>> it "should explicitly set created_by" do
>>>  controller.stub(:current_user) { mock_user }
>>>  mock_order.should_receive(:created_by=).with(mock_user)
>>>  post :create
>>> end
>> 
>>> This is my newly working spec. Does this look well thought out or is
>>> there some glaring pitfall to doing it this way?
>> 
>> Well, this is the way mocking and stubbing works. If you want to set an 
>> expectation on an object that doesn't exist yet at the time you're setting 
>> up the spec, you have to "chain" things so as to inject a mock of your own 
>> at the right place, which you've done here. The amount of work you have to 
>> do setting this up will vary from case to case.
>> 
>> This is one of the costs of the interaction-based approached, and you have 
>> to weigh up that cost against the benefits.
> 
> Out of curiosity, how would you do this with a state based approach?
> I've tried checking the value of created_by after post :create but my
> assign(:order) has no idea what :created_by is.
> assigns(:order).created_by.should eq(mock_current_user) just doesn't
> do what I thought it would.

Ok, some qualifications:

- I use factories heavily (in this example Factory Girl) to make it easier to 
write tests. I don't worry about the slowness of using real model instances 
until the spec suite actually becomes large enough for it to be a problem. I 
also don't worry about the risk of cascading failures (broken model breaks 
controller spec) because I value the simplicity that comes with keeping mocking 
and stubbing to a minimum in the specs, and I am wary of the insulation that 
mocking and stubbing bring and can actually hide real failures (eg. API changes 
but spec keeps passing anyway). I still do mock and stub, but generally only 
where it is not easier to just verify state (and it often is easier).

- I use RR for mocking because I like the syntax. Even in a state-based 
approach, you'll see I have to stub out the "current_user" method on the 
controller.

- I use a "before" block that sets up @user, @params, and does the stubbing, so 
that I can reuse the same stuff in many different "it" blocks.

- And I try to keep each "it" block as short as possible, just do the post then 
inspect the state afterwards. In this case the state that I am checking for is 
the externally visible stuff like what got assigned (assigns), what got 
rendered (render_template) or redirected (redirect_to), what flash or cookies 
got set, and so on. If need be, I can query the database.

- And finally, note that this is written for Rails 3/RSpec 2, which I've been 
using solidly for the last month and have now forgotten what Rails 2/RSpec 1 
specs looked like!

So with all that said, this is more or less what my state-based approach would 
look like:

  before do
    @user = User.make!
    stub(controller).current_user { @user }
    @params = { :post => { :title => 'foo', :body => 'bar' } }
  end
  
  it 'should set created_by' do
    post :create, @params
    assigns[:post].created_by.should == @user
  end
  
  ...

The controller/action is treated as a "black box" which I never look inside. 
Each "it" block basically just follows this pattern:

  1. Feed params into the black box
  2. Check _one_ piece of state afterwards

By trying to keep only one "should" assertion in each "it" block I get nice 
granularity in the event of failure.

It is not "better" nor "the right" way, it is just "a" way of doing it. I've 
also written controller specs where I ended up mocking left, right and center, 
but if I can take the state-based approach I generally prefer it. The app I'm 
currently working on has about 3,000 examples, and the suite runs fast enough 
that I don't yet feel the need to change my emphasis away from state-based 
testing.

Cheers,
Wincent

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

Reply via email to