On 2010-07-09 8:38 AM, Wincent Colaiuta wrote:
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.


I'm curious, Wincent, what you consider to be "fast enough"? You have about twice as many examples as I have in a project, and I'm starting to think that it's taking too long to run the whole suite. I do tons of mocking and stubbing (and grow very weary of it), but can't imagine how long my suite would take if I didn't. Currently, if I run with RCov, it's in the 6-7 minute range. Without RCov, it's between 5-6 depending on whether it's running in Ruby 1.8.7 or 1.9.1 (1.9.1 being faster). I am using PostgreSQL in testing, though, so I know that's adding a bit. I was using SQLite in testing until recently, and that was still up over 3-4 minutes. [I switched to PostgreSQL in testing so I could take advantage of PostgreSQL specific features. There is zero chance that this app will ever need to run on anything other than PG, so portability is not a concern.] There are a couple of tests that take quite a bit of time (one generates a PDF and verifies that it is where it is supposed to be and another creates a bunch of records to calculate statistics for), but even without them, the suite is taking over three minutes.

So how long do your 3k examples take?

Peace,
Phillip

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

Reply via email to