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
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users