On Jun 28, 2008, at 7:27 PM, Britt Mileshosky wrote:

----------------------------------------
Date: Sat, 28 Jun 2008 18:24:17 -0500
From: [EMAIL PROTECTED]
To: [email protected]
Subject: Re: [rspec-users] Stopping example execution?

On Sat, Jun 28, 2008 at 5:57 PM, Britt Mileshosky
wrote:

Hello, I'm wondering If I am missing something here when creating an example that sets an expecation at the top or beginning of an action but requires you to stub / mock everything that follows.

Example:
I want to test that a certain controller is running a before_filter...thats easy:

- controller.should_receive(:require_user)
- do_get

But now i've got to mock / stub everything else that comes behind this filter so that I don't receive 'unexpected method' errors, or other blowups because I am requesting the whole action. Is there anyway to stop execution after an expectation has been met? It seems to me that this might clean things up a bit. Not sure, I'm still fairly new to BDD/Mocking by about 2 weeks.

Yep, you can stub out the requested action on the controller. Say
you're testing that the :index action requires authentication:

 controller.should_not_receive(:index)
 stub_not_logged_in
 do_get

Or the opposite:

 controller.should_receive(:index)
 stub_logged_in
 do_get

Personally I prefer expecting the action instead of expecting the
filters, but I think both would accomplish the same goal, as long as
you tested the filter on its own. If you just wanted to stub out the
action to prevent it from doing anything, you could of course just use
controller.stub!(:index).

HTH

k
_______________________________________________
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users


So i did something like.

- controller.should_receive(:before_filter_action)
- controller.stub!(:action_after_filter)
- do_get

Works nicely... but if you think about it and look closely, we are still stubbing methods after the intended expectation was met. This method just happens to encapsulate a whole other set of methods, which is why it works nicely.

But lets say i have something like this

---------------------------------------------------------------------------------------------------

def some_action
   @user = self.current_user
   @account = self.current_account if self.has_account?
   @person = @account.people.find(params[:person])
end

---------------------------------------------------------------------------------------------------

describe "with a logged in user"

 before(:each) do
   controller.stub!(:current_account)
   @account = stub_model(UserAccount)  # Shouldn't have to stub here?
@person = stub_model(User) # Shouldn't have to stub here? @people = mock("list of people") # Shouldn't have to stub here? @people.stub!(find) # Shouldn't have to stub here? @account.stub!(:people).and_return(@people) # Shouldn't have to stub here?
 end

 it "should find current user" do
   controller.should_receive(:current_user).and_return(@mockUser)
   do_get
 end

 describe "who has an account" do
   ... should put account stubbing here with examples


  describe "which has people" do
    ...  should put people stubbing here with examples
  end

  describe "which has 0 people" do
    ...
  end


 end

 describe "who doesnt have an account" do
    ...
 end

end

------------------------------------------------------------------------------------------------------------------------------

Notice I have to stub everything out at the first before(:each) declaration because my "should find current user" example will blow up because of unexpected methods after the expectation. All I want to know is that the current user is expected to be found and stop. My examples LATER should define stubs and expectations.

I'd like something like

- controller .should_receive(:current_user).and_return(@mockUser).end_example

Am i going about it all wrong? Is there something I'm missing or does this just seem natural...


You're doing the right thing by handling the stubs before(:each) - that's what it's therefore - to set up the environment so things will run.

I would recommend avoiding instance variable assignment there, however. If you need a variable, create it directly in the example. This is something that I've only recently started doing and I find that it keeps things much more clear.

There are a few things you can do to tidy up the stubs a bit. You could organize things differently to express the relationships better:

before(:each) do
  controller.stub!(:current_user).and_return(stub_model(User))
  controller.stub!(:has_account?).and_return(true)
  controller.stub!(:current_account).and_return(
    stub_model(UserAccount,
      :people => stub('people',
        :find => stub_model(Person)
      )
    )
  )
end

If you wrap self.people.find in self.find_person in Account (which fixes the Law of Demeter violation), like this:

def some_action
   @user = self.current_user
   @account = self.current_account if self.has_account?
   @person = @account.find_person(params[:person])
end

... you can reduce the before to this:

before(:each) do
  controller.stub!(:current_user).and_return(stub_model(User))
  controller.stub!(:has_account?).and_return(true)
  controller.stub!(:current_account).and_return(
    stub_model(UserAccount,
      :find_person => stub_model(Person)
    )
  )
end

In cases like current_user, you don't really need to stub that because the controller will just return nil.

If has_account? actually checks for current_account.nil?, then you don't have to stub that either. Now you just have this:

before(:each) do
  controller.stub!(:current_account).and_return(
    stub_model(UserAccount,
      :find_person => stub_model(Person)
    )
  )
end

This is a *bit* more organized, but still quite busy. In the end, when you're dealing with code that violates Tell, Don't Ask (which is quite natural in Rails controllers), you have to stub a lot of stuff to get the isolation you want. It's a tradeoff.

HTH,
David
_______________________________________________
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to