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

Zach Dennis zach.dennis at gmail.com
Thu Sep 4 21:25:16 EDT 2008

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"
            source_account.deposit amount
            target_account.withdraw amount

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"
            source_account.deposit amount
            target_account.withdraw amount

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

More information about the rspec-users mailing list