Pat Maddox, B.D.D.M.F.

RSpec my authority! I just smashed a bug

Spikes, Test-after, and Learning How to Test

Last month, I had the pleasure of sitting on a panel on software

testing along with Tammer Saleh, Bryan

Liles, and Sandi Metz.

It was a lot of fun. They’re a great group of folks and we had plenty

of interesting discussion about testing.

One of the questions we talked about was, “Bryan says to test all the

fucking

time.

ALL the time? Really? What about when writing

spikes?”

Tammer suggested that you write your spike without tests and comment out all the code

when you’re done. You then start writing tests and uncomment only the

code that you need to make it pass, refactoring along the way. I love

this strategy when dealing with existing, untested production

code, but when Obie asked

if I had any thoughts, my response was, “Um, I basically hate that

idea.”

TDD and spikes are design tools. Test-driving your code is effective

with “design in the small,” helping you build objects that are

cohesive, loosely-coupled, and communicate via intention-revealing

interfaces. In

order to do this effectively though, you typically have to have some

idea of what you’re going to build. Personally, I find TDD to be an

excellent exploratory/discovery tool, but even I have a very tough

time being effective when I work with objects at a low-level if I

don’t have a higher-level picture in my mind. When that picture is

fuzzy or blank, spikes become invaluable. I can just hack my way to a

prototype. The code quality doesn’t really matter, because my main

goal is answering the question “how the hell am I going to do this?”

When I’m through spiking, I throw it away. Or at least commit it to a

temporary branch so I can reference it if I need to. I want to be

free from that code so I can combine the insight I’ve received from

the spike with the usual design benefits of TDD. If I leave that code

in place, it’s too easy for me to think that it’s good enough if I

just get it under test now. That’s not how you get the max benefit

from TDD.

BUT

Commenting out the code and then writing tests does give you a degree

of freedom that I hadn’t thought of during that panel. Probably my

favorite thing about TDD is that it lets me mentally separate the What

from the How. Figuring out which tests to write - specifying the

What - is frequently more difficult than making them pass. Tammer’s

approach gives you a great opportunity to write the tests you wish

could have written before…without the extra work of having to make

them pass. You can get moving pretty quickly, writing a test and

uncommenting a couple lines, rinse, repeat, with only occasional

slowdowns as you nudge the implementation a bit.

Another thing that didn’t dawn on me until recently is that the good

TDDers I’ve met are all good hackers as well. I suspect it’s because

good hackers quickly learn that the easiest way to make sure your code

still works is to write automated tests, and the easiest way to write

automated tests is to write them before the code. What this means is

that when Tammer writes a spike, the end result is going to be pretty

good code that gets the job done. From there, he writes the tests to

give him a safety net which he can use to refactor the code to make it

even better.

Test-after as a means of learning how to test

Writing code and testing it afterwards isn’t just for spikes though.

I think it can also be a useful strategy for learning how to write

tests. If you’ve been coding Ruby for the last two years but don’t

know RSpec (or test::unit, or shoulda, etc..), you’re going to have to

go through the learning curve, plain and simple. This means learning

new syntax, libraries and idioms. On the RSpec list, I occasionally

see questions like, “I wrote this test but it’s failing, but I know

the code works fine, why is RSpec stupid?” The problem comes from

thinking that the author wrote the test correctly in the first

place…clearly that’s not the case. By writing the code first and

then the tests, you get the effect of TDD applied to learning a

testing framework.

Now I want to make it absolutely clear that I don’t advocate

test-after as your main strategy. I do think that it can be

combined with spikes to write well-designed production code. And I

think it can be useful for learning how to test as well. The fact

is that TDD is hard to do well, and as with any difficult technique

requires practice. You’re not going to get there overnight, and it’s

perfectly fine to fudge the process if that’s what it takes to

develop your skills. Just be sure to practice real TDD too, so you

can eventually be test-driving by default.