[rspec-users] Assumption tests

Pat Maddox pergesu at gmail.com
Sat Oct 20 14:54:56 EDT 2007


On 10/20/07, Daniel Tenner <daniel.ruby at tenner.org> wrote:
> On 20 Oct 2007, at 17:34 20 Oct 2007, David Chelimsky wrote:
>
> > Agreed. This is exactly why we talk about stories and specs instead of
> > integration and units. I realize that I've slung the term integration
> > tests around when talking about about stories so I apologize if I've
> > added to the confusion.
> >
> > The distinction we make between stories and specs is that stories
> > describe how a system behaves in terms of a user's experience, whereas
> > specs describe how an object behaves in terms of another object's
> > experience.
> >
>
> If a spec describes how an object behaves in terms of another
> object's experience, doesn't that mean that a spec describes an
> object's interactions with other objects?

Sometimes yes, sometimes no.  Not every method of every object needs
to interact with another object.  Eventually you want to have several
objects that are able to each do one thing and do it well, without
needing to ask other objects for help.


> I know I keep coming back to this. I promise I'm not doing it to be a
> pain, but because I'm curious about whether there is something in
> pushing this thing to extreme and saying "it's not a spec unless it
> describes what the object _does_" (as opposed to an outcome once the
> object does something).

It's important to think about and explore different ideas, but you're
taking this too far.  Taking the example you gave in IRC yesterday:

@account.instance_variable_set(:@balance, mock_balance)
mock_balance.should_receive(:-=).with(300)
@account.withdraw 300

That is just an insane level of granularity.  You're not specifying
what the object does, you're specifying how it does it.  _what_ it
does is withdraw - we express that through good naming.  Now you need
some way of verifying that it actually does that properly.  You can
explicitly do it by setting the instance variable to a mock and
setting an expectation on it, or you can do it implicitly by
exercising another behavior of the object - in the case of account,
expecting an ending balance.

In this case, you're specifying the implementation rather than the
behavior.  If we were writing some app that made use of an Account
class, we would have gotten to a point where the Account is
sufficiently granular enough that you can safely use state-based
verification.  Account has a specific job, and the details of how it
does that job is its own responsibility, and for nobody else to know
anything about.  That's what we call encapsulation.

> Here's where I'm coming from. Take http://behaviour-driven.org/
> TDDAdoptionProfile as the compass of where we're going. Then, if
> we're only using BDD as "TDD done right", we're actually still stuck
> at step 5, testing what the object looks like after we prod it in a
> certain way. Instead, we should be defining behaviour, ie what the
> object actually does, both internally and externally, in response to
> a prod.

You seem to believe that the only way to define behavior is in terms
of interactions with other objects.  That is flat-out wrong.  Please
read http://martinfowler.com/articles/mocksArentStubs.html.



> I guess the question someone could ask here is "if your specs are not
> proving that your system works, what do they prove?" - my answer to
> that is that they prove that my code is still doing what I specified
> it should do - e.g. if i specified that when I call a certain method
> the current user should receive a :delete call, the spec ensures that
> this continues to happen no matter what other things i specify that
> that method should do.

And that's fine, when you're specifying interactions between objects
of different levels of abstraction.  Objects in one level should think
of objects in a lower level only in terms of interface.  But you'll
also find that those sorts of specs tend to be more brittle, and
rightly so - interfaces are quite simply more difficult to change,
because there are other objects that depend on that interface being
consistent.  What you're doing is applying this at an extreme level,
which leads to brittle specs that don't add any value.

Honestly, your Account#withdraw example from IRC yesterday still has
me bewildered.  It's an example of particularly gross
overspecification, and maintaining that sort of codebase would be a
nightmare!  That's why I say there's no room for dogmatically trying
to apply terms like "unit" or "integration."  The whole point of agile
development is to deliver software that satisfies the customer.
Nothing else matters.  There are only tools that help us achieve that
goal, but they can most certainly hurt us if used improperly.

Pat


More information about the rspec-users mailing list