[rspec-users] Four Question From an RSpec Baby - Give me something to chew

Dan North tastapod at gmail.com
Fri Sep 5 15:24:19 EDT 2008


I think that's one of the nicest descriptions of the value of outside-in
I've seen.

Thanks Zach.

2008/9/5 Zach Dennis <zach.dennis at gmail.com>

> On Thu, Sep 4, 2008 at 7:14 PM, Nick Hoffman <nick at deadorange.com> wrote:
> > On 2008-08-27, at 15:25, Mark Wilden wrote:
> >>
> >> The other thing I would say is that mocking and stubbing are powerful
> >> tools that you should add to your arsenal as soon as possible. I've had
> >> several coworkers who resisted using them, only to finally achieve that
> >> "aha!" moment later. Your tests get easier to write, and they're less
> >> brittle to change.
> >
> > G'day Mark. I was re-reading this thread and noticed this paragraph of
> > yours. I've been using RSpec and BDD for about 2 months now, and love it.
> >
> > However, I'm not a fan of mocking and stubbing, primarily for two
> reasons:
> > 1) I believe that specs should test behaviour, rather than a behaviour's
> > implementation.
>
> This is a misleading statement. Testing the behavior of an object
> involves its input and output collaborators. When used appropriately
> mocks and stubs act as an agent for design and discovery. They also
> allow objects under test to be isolated from other objects that it
> depends on (which may not even exist yet).
>
> Collaborators are what should be used to set up with stubs and mock
> expectations. This way we can test the behavior of the object under
> test given different values returned by its collaborators.
>
>
> For example:
>
>   class FundsTransfer
>      def transfer(amount, source_account, target_account)
>         if source_account.balance < amount
>            raise "not enough money"
>         else
>            source_account.deposit amount
>            target_account.withdraw amount
>      end
>   end
>
> Without using mocks or stubs to test the above code I wouldn't be able
> to test the case where the source account doesn't have enough money or
> does have enough money -- unless I already have implemented the
> Account class and its withdraw, deposit and balance methods. Taking
> the approach of writing the Account class first is an approach that a
> lot of developers take. But you build what you think you need before
> you actually need it. I prefer to develop from the other direction,
> only building what I discover I need to make it work.  Either way
> though behavior is being tested, and it is not testing against the
> internal state of an object.
>
> > 2) Using mocks and stubs causes your specs and implementation to be
> tightly
> > coupled, which often forces you to modify your specs if changes occur in
> the
> > implementation.
> >
> > However, #2 contradicts what you said about "tests ... [are] less brittle
> to
> > change" when using mocks and stubs. Considering that I'm still very new
> to
> > mocks and stubs, I'm probably missing something here. When you have a
> > minute, would you mind countering me?
>
> You can write bad tests with mocks/stubs and without mocks/stubs.
> Using mocks/stubs doesn't immediately tightly couple your specs to
> your implementation. It does however tie your specs to the external
> interface of the object under test. If you change that, then you will
> need update your specs.
>
> Consider our FundsTransfer example again:
>
>   class FundsTransfer
>      def transfer(amount, source_account, target_account)
>         if source_account.balance < amount
>            raise "not enough money"
>         else
>            source_account.deposit amount
>            target_account.withdraw amount
>      end
>   end
>
> If I supply a source_account that has a stubbed out balance of less
> than the amount requesting to be transferred am I tightly coupling the
> spec to the implementation? No more so then creating a source account
> fixture with an amount less than the amount that I am requesting to
> transfer. I say this because at some point you need to test the
> scenario where funds are requested to be transferred from a source
> account that doesn't have enough money.
>
> Do you really see one of the below examples more tightly coupling the
> test to the implementation?
>
>    account = mock("Account", :balance => 0)
>    account = Account.new :balance => 0
>
> The difference to me is that using the mock allows me to discover
> earlier what interface I want to work with on the Account class. If I
> go the non-mock route then I need to make sure I build the Account
> class with the interfaces I need first.
>
> I much prefer to work in small steps. Focus on one behaviour, and then
> when it works as expected, make sure any collaborators that need to be
> implemented are. Then use my integration tests to make sure everything
> is wired up correctly together.
>
> That's just me though,
>
> --
> Zach Dennis
> http://www.continuousthinking.com
> http://www.mutuallyhuman.com
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20080905/5847f13d/attachment-0001.html>


More information about the rspec-users mailing list