Topic: Having trouble finding the balance with Mocking/Stubbing and .new

So far I've been stumbling along at a reasonable pace introducing RSpec into an already-existing project (backwards I know but I have my reasons!).  In this application "Facilitators" sign up under an "Account".  I've been able to write RSpec tests to ensure all the usual login stuff works properly and that errors come up as they should and redirects happen as they should, but a recent bug that was discovered made me want to write an RSpec test to check for it to ensure it doesn't happen again.

Unfortunately in order for me to write an RSpec test to "fail" on this bug I need to (I think) first re-write how I've done my mocking/stubbing because I currently Stub too close to the top layer of things to catch this issue.  Meaning, I'm stubbing the process where this problem occurred.  Now I just can't figure out how to re-write things to work!

Here's part of my RSpec test file:

  before do
    @test_account = Account.new(:subdomain => 'test', :name => 'TEST')
    @test_account.admin_id = 1
    @test_account.id = 1
    Account.should_receive(:find_by_subdomain).and_return(@test_account)
   
    @facilitator = mock("facilitator")
    add_stubs(@facilitator, :email => 'facilitator_email', :password => 'facilitator_pass', :class => Facilitator, :account_id => 1)
    @facilitator.stub!(:authenticate).and_return(true)
   
    @wrong_account_facilitator = mock("facilitator")
    add_stubs(@wrong_account_facilitator, :email => 'facilitator_email', :password => 'facilitator_pass', :class => Facilitator, :account_id => 2)
    @facilitator.stub!(:authenticate).and_return(false)
  end
  it "should login facilitator and redirect with valid email and password" do
    post :create, :email => @facilitator.email, :password => @facilitator.password
   
    session[:user].should_not be_nil
    session[:user_type].should == 'Facilitator'
    response.should redirect_to('/')
  end

And here is the call where the bug lived:
self.current_user = current_account.facilitators.authenticate(params[:email], params[:password])

The "facilitator.stub!(:authenticate)" call is a new attempt that replaced me using "Facilitator.should_receive(:authenticate).and_return(@facilitator)" in the test itself.  The problem (as should be pretty obvious) was that by me stubbing out the :authenticate was that for ANY Account it would return my specific Facilitator.  "current_account.facilitators" should NOT find my Facilitator if the account is different than the one this Facilitator belongs to.

To fix this I'm pretty sure I need to stub at a lower level of the code and stub where the Facilitator.authenticate (singular) call is made.  Unfortunately this now means I need to stub the Account's :facilitators call as well in order for it to actually establish the has_many thing and I can't figure out how to do that.

I feel like I'm falling down a slippery slope of stubbing out everything when I should just be using .new and creating these things without stubbing them.  I was using fixtures and that worked just fine but everything I read tells me to get away from fixtures.

I know this is a rather long ramble but to ask my question in a more concise way, should I be stubbing more items?  Should I be stubbing in a different place?  Should I not be stubbing and should I instead use .new or just stick with fixtures?  Thanks!

Re: Having trouble finding the balance with Mocking/Stubbing and .new

Flipping things back to the way BDD was intended, what is the behavior you expect of logging in with a valid email and password? It seems you are going way back into the implementation details with your stubs. Presumably:

- All the authentication is handled at the model level and can be tested in model tests
- The only thing you need to ascertain is whether a valid user gets redirected to the home page

I try to concentrate all the authentication into something like an authenticated? method that I can easily describe in model specs and then rely on those specs to cover its behavior. For the controller, I just use expectations on the model (partial mocks) or mock_models.

I'm grappling with the same thing myself having to set up a labyrinth of mocks and stubs to make it possible to describe a has_many :through association. My current thinking -- just mine -- is that if I'm likely to spend more time debugging my mocks and stubs than the fixtures, I'll use the fixtures. As soon as I figure out how to make rathole ... um, foxy fixtures work with has_many :through.

Seriously, specs are about avoiding bugs, not creating elaborate one-off mock frameworks just to allow one spec to pass. As you note, there is always a way to stub out enough stuff to get a spec to pass. That could obscure bugs in other layers of code that would have shown up had you not been required to exert all your cleverness on your mocking and stubbing.

And I'm a fan of mocks and stubs!