I consider myself an intermediate-to-advanced rubyist and rails
programmer. I’ve embraced TDD but sometimes wonder if I’m just doing
it wrong. I feel like it can take me longer to get the specs done
then should be necessary.
I’ll give a very specific example from a project I was working on last
night. I was working on a feature in a personal budget-management
application to be able to deposit and withdraw funds from/to an
‘account’.
I wrote a request/integration spec using capybara to go through the
interaction from a browser’s point of view - I went through every
possibility:
If a user deposits funds, the new amount should be displayed for the
account
If a user withdraws funds:
If there are enough funds remaining show the new amount
If there are not enough funds only allow the amount field to become
negative if the negative_overflow_id is the account itself (show the
new amount)
If there are not enough funds and the negative_overflow_id is
another account, remove the remaining amount from that account and
display the new amounts on both this account and the other account.
If there are not enough funds and no negative_overflow_id is set,
return an error on the field to the user indicating the problem.
This part I’m ok with, I think there should always be a walkthrough of
the full process that takes the entire stack into account. Since I’m
somewhat thorough in my request specs, I tend to skip view tests and
most of the controller tests (I’ll throw in some controller tests for
certain cases) because all of them together just take an inordinate
amount of time to finish.
I run the tests, I get red. I throw together the view and controller
enough so that the tests give me this error:
undefined method update_amount on Account
My controller is thin, and the view is simple. One field (amount)
with two submit buttons (“Withdraw” and “Deposit”). The controller
does this:
if @account.update_amount(params[:account][:amount], params[:commit])
All is well in the world. Now I drop down to model specs. I write
all of the expected cases for the model first before even defining the
method. (Is this how you’re supposed to do it?
My Account model spec ends up having 27 assertions (27 separate it
“should” blocks, mind you - I’ve joined the ‘don’t put multiple
assertions in one it block’ camp) for this one method.
The method appears that it is going to attempt to do too much, and I’m
very aware of this as I’m writing the specs. But I want to have the
‘end result’ mapped out before I write up the method.
My method, when all is said and done, ends up looking like this:
To implement the edge cases defined above I setup some before
validation and before update filters. My spec for the method passes,
so I know it’s doing what I want (in some fashion), but as I was
writing the code to make it work I failed to write any specs for the
callbacks that the model makes to implement the functionality.
I’m not sure what path I should have taken instead. Some camps seem
to think that you should stub off and use .should_receive calls for
specific behavior in a tested method to keep it isolated… but then
how do you really know that it’s adhering to the other methods’
parameters and such? Also if you do that, aren’t you mocking up
implementation in your spec which your test shouldn’t care about at
all (only the end result).
Should I keep this test with it’s 27 assertions, and then write
another 20 or so for all of the callback methods that help to
implement it’s behavior? It takes so long to get the request specs
and method specs written and then I end up having to re-write many of
them because of how I implemented the method.
Does anyone have thoughts on how this is supposed to be done in a TDD
fashion?