[rspec-users] Assumption tests

David Chelimsky dchelimsky at gmail.com
Sat Oct 20 16:00:14 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:
> > 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?

Not necessarily.

When I say "from another object's perspective" I mean that the spec is
acting as another object, whereas in a Story, the story is acting as a
human user. Or, at the very least, an external user (a person or
another program).

Again, I think you're wanting to equate interaction testing with BDD.
While we do espouse using mocks as a discovery tool, that doesn't mean
they are the only tool.

Also - there is the never ending debate about whether mocks should
stay in place once the real objects do come to life. There is no one
answer to that question. The trick is to balance a few things:

1. mocks make your specs more brittle (encourages removing mocks)
2. mocks help you focus on the behaviour of one object rather than
that object and it's collaborators (encourages leaving mocks)
3. mocks help isolate you from expensive parts of the system
(encourages leaving mocks)

Keep in mind too (I think I've brought this up before) that if
foo_collection#<< appends a foo to its internal Array, you would
likely not want to do this:

describe FooCollection do
  it "should store a foo in its internal Array" do
    internal_array = mock("internal array")
    mock_foo = mock("foo")
    foo_collection = FooCollection.new(internal_array)
    internal_array.should_receive(:<<).with(mock_foo)
    foo_collection << mock_foo
  end
end

If, however, adding to a collection meant making a call to an external
service (database, web service, etc) you might do this:

describe FooCollection do
  it "should message the service when it gets a new foo" do
    foo_service = mock("foo service")
    mock_foo = mock("foo")
    foo_collection = FooCollection.new(foo_service)
    foo_service.should_receive(:add_foo).with(mock_foo)
    foo_collection << mock_foo
  end
end

These two specs are basically the same, and I can tell you that I
would likely NOT write the first one, but I would very likely write
the second one. This means that my decision is based on the
implementation, which might bug our purist BDD sensibilities.

Which brings us to an important point. As is software in general, BDD
is all about balance. It requires thought. It requires weighing
opposing forces and making a practical decision.

Also keep in mind that all of the principles that we espouse can be
traced to a single source: productivity. It boils down to trying to
come up with practices that save our employers money. So no matter how
pure or beautiful something appears, if it ends up increasing project
costs (and I mean long term, from conception to obsolescence) then
it's not a good candidate for adoption. Similarly, if something
decreases long term project costs, then it's worth looking at, even if
it violates some other sensibilities that we've already developed.

Cheers,
David


More information about the rspec-users mailing list