Assumption tests

On Oct 20, 2007, at 4:29 PM, David C. wrote:

opposing forces and making a practical decision.
Perhaps there are solutions for specific libraries - e.g.
ActiveRecord, but even then you’re going to have to specify structure
in advance (has_many vs has_many through) and consequently change your
specs when you decide to change your model.

Sure - I’m only advocating a solution to AR, not a more a general
one. How many other (ruby) apps are built on top of a giant DSL?

A has_many call is a macro - it generates 9 methods. I don’t see a
big problem with changing it in one place. It’s a much bigger deal
if you have to change the custom stubs you’ve created for that one
macro is 9 specs.

These common patterns (for Rails) should be abstracted away
(as some have been doing with Rails’ association proxy). There is no
reason that all of those implementation details of another library
need to be crowding the intention of my specs. Until this happens, I
need to know all of these details about how another library works,
which, for me, is too far off the scale (no balance there)…

We’re all looking forward to your matcher libraries :slight_smile:

Not going to happen soon, because I don’t care to dig into the
internals of ActiveRecord. Maybe if I get really frustrate, it will
happen, but for now I’m hoping someone else in the community with
more AR experience will come up with something like this.

Scott

On 10/20/07, Daniel T. [email protected] wrote:

I’m all for pragmatism but it kind of irks me that I’d have to test
behaviours in some cases and outcomes in others. I suppose neither of
them is black nor white, and David’s suggestion that it’s all down to
balancing design forces on a case-by-case basis…

You need to carefully analyze your situation and selectively apply the
tools you have at hand. This is what I meant earlier when I said it
becomes too easy to use process and tools as a crutch for
thoughtfulness. It may sound harsh, but that’s absolutely what you’re
trying to do.

Also, it’s not testing behavior vs outcome. It’s testing interaction
vs outcome, which both fall under the umbrella of behavior.

Pat

On 10/20/2007 4:37 PM, Scott T. wrote:

Sure - I’m only advocating a solution to AR, not a more a general
one. How many other (ruby) apps are built on top of a giant DSL?

Not going to happen soon, because I don’t care to dig into the
internals of ActiveRecord. Maybe if I get really frustrate, it will
happen, but for now I’m hoping someone else in the community with
more AR experience will come up with something like this.

I keep threatening (in my head) to build a “proper” mock of at least
part of AR, but it just keeps joining the large list of open source
tools I use that I’m gonna go “fix” someday…

Jay L.

On 20 Oct 2007, at 19:54 20 Oct 2007, Pat M. wrote:

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.

Thanks for that excellent link. I hadn’t read it yet. I need to think
some more :slight_smile:

I think one of the reasons I’ve tended towards all-out behaviour
mocking is that when you start mocking expectations, you often break
outcome-based testing. As a good example, using another variation of
that Account object…

class Account
def initialize(balance_holder)
@balance_holder = balance_holder
end

def balance
@balance_holder.calculate_balance
end

def withdraw(amount)
@balance_holder.decrease_by amount
end
end

Now if @balance_holder is a pretty complex, slow object that cries
out to be mocked, trying to test in the way that you suggested breaks
down:

it “should decrease the balance when an amount is withdrawn” do
@mock_balance_holder.should_receive(:decrease_by).with(100)
account = Account.new(@mock_balance_holder)
account.withdraw(100)
end

In a case like this, it seems to me impossible to avoid specifying
only behaviour, unless you actually create a full-on fake object to
fake the behaviour of the balance_holder (which could be a bit less
trivial than this). But if you’ve mocked the balance_holder like
that, it is impossible to then test the state of the Account.

I guess the issue comes from situations where the apparently internal
state of an object is dependent on the state of another object. In my
case, I have this happen fairly often when my facebook users, which
depend on a nasty, bug-eyed facebook_session object that I definitely
don’t want to interact with in my specs (at least not with the real
version, which is horrendously slow and bug-prone due to various
facebook peculiarities). I don’t think that’s wrong design, but it
does mean that in those cases you can’t use outcome-testing at all
(unless you are writing an integration test).

I’m all for pragmatism but it kind of irks me that I’d have to test
behaviours in some cases and outcomes in others. I suppose neither of
them is black nor white, and David’s suggestion that it’s all down to
balancing design forces on a case-by-case basis…

Maybe I should get back into maths, so I can have some absolute
truths again :wink:

Thanks to everyone for the very useful discussion, by the way. This
is very helpful, and I’ll try to summarize this thought progression
on my blog so that it’s not lost…

Daniel

On 10/20/2007 12:09 PM, Pat M. wrote:

I’ve read plenty of
sources that say a unit test only touches one class, and if you’re
interacting with more than one class then it’s an integration test.
That’s a silly distinction to make

Very! I think we all know here that a unit test tests your model, a
functional test tests your controller and model, and an integration test
tests your views and your controllers and your models.

Jay

On 10/20/07, Jay L. [email protected] wrote:

On 10/20/2007 12:09 PM, Pat M. wrote:

I’ve read plenty of
sources that say a unit test only touches one class, and if you’re
interacting with more than one class then it’s an integration test.
That’s a silly distinction to make

Very! I think we all know here that a unit test tests your model, a
functional test tests your controller and model, and an integration test
tests your views and your controllers and your models.

:slight_smile:

Just thought I’d post an update on this, since I promised Pat that I
would :slight_smile:

Ultra-detailed, “pure behaviour/interaction” specs the way I wanted
to do them has turned out to be unproductive in the long run. The
specs are too complex to write, and too hard to read, and the lack of
refactoring tools that understand rspec means they actually hinder
refactoring. Basically, they become essentially useless and a time-
consuming hindrance. (yikes!)

So, I’m now using a pragmatic middle-ground… I start off with the
idea that the spec is actually an outcome test, but I use a lot of
mocking whenever I feel I want it (for isolation purposes or when the
object doesn’t exist yet). This means a fair bit of “behaviour/
interaction” specification seeps in, but not so much as to take over
completely like with my previous approach.

In conclusion, you were right - my approach didn’t work out. The
extreme, “pure” approach doesn’t work, and the best (quickest to
write, easiest to read, and most useful to execute) I’ve found is a
middle ground based on intuition and experience.

Well, at least I’m definitely not afraid of mocking now :slight_smile:

Thanks for the discussion last month!

Daniel
#swombat

On Oct 21, 2007, at 1:12 am, Jay L. wrote:

I keep threatening (in my head) to build a “proper” mock of at least
part of AR, but it just keeps joining the large list of open source
tools I use that I’m gonna go “fix” someday…

Mock ActiveRecord? Hell to that, I’ve had enough of DHH’s little
kludge. I’m gonna write a new ORM layer that CAN be mocked…


blog @ http://aviewfromafar.net/
linked-in @ http://www.linkedin.com/in/ashleymoran
currently @ home