The case for TDD

Posted by Joe Wilson on Friday, July 17, 2009 7:39 PM

TDD is hard. It sounds like it wouldn't be hard.  It's just a little test.  The code is the hard part, right?  But it's really hard to do it well. I struggle with it plenty and I know my way around C# and OO pretty well.

There better be a payoff for this bass-ackwards way of coding.  It takes a while to get there, but I can see the light at the end of the tunnel in my own development.  Here's where I've seen my code improve as I've moved toward TDD:

Focusness

You focus on the behavior of your code instead of the implementation.  You don't start by thinking of tables, classes, and properties (or in my case, try hard not to).  You start off thinking of the business requirements and roles.  User stories are popular because they push you into restating the requirements in context: "As a (some role)...I want to (do something)...so that (some business value)".  The first two chunks of this can guide your test creation.  You can write methods that cover these scenarios.

TDD Logo If you're doing it the RED-GREEN-REFACTOR way, you'll write a test and just enough for the code to compile, get the test to pass with the minimum amount of code possible.  Finally, you can go back and clean up what you aren't proud of from your first effort, then run the test again.  You get to keep repeating that until you are happy with the code and that the requirement/story is met and the user accepts it.

Then you STOP.  That's the fourth step you don't see in the pretty RED-GREEN-REFACTOR icon.  If you don't stop, you loose your focus on the functionality and you start trying to gold-plate the code.  Which can lead to...

Dude, you know what would make this REALLY cool?

You're getting paid to write software to meet the requirements.  If you going beyond that, be careful who you're doing the coding for.  If it's the business, and you're glad to show them how much time you've spent on it, you might be justified in doing it.  But if you're coding it for yourself, do it on your own time.

Unfortunately, people who are drawn to programming are often tinkerers and bit-twiddlers.  I appreciate having a framework for my development that keeps me on task, because I am easily distracted by grand visions of what I might be able to code into the app.  There is no code we couldn't tweak a little and add this new cool pattern or just-learned technique to.  TDD forces you to go directly to the requirements, not the technology.

SOLID code

The more you move toward TDD, the more little classes you have with giant names.  Same thing with methods.  There are tons, they are very short (one screen or so max), and they have long names.  This is progress?

Yes, I think it is because of the SOLID principals.  SOLID is a horrible acronym of acronyms, but it can help you keep some object-oriented principals in mind.  I'll go over it and how I've tried to learn it in more depth in a future post, but the guys at Los Techies covered SOLID in depth already.  So the side effect of decoupled code is you have lots of little files that just do one little thing.  It took me a while to get over this and the file bloat in the project, but I'm at peace with it now. The payoff is that I am using more SOLID principals than I was before, and that's a good thing.

Oh yeah, you get some tests, too

It's been said before, but TDD is kind of misnamed.  Yes, you start with the test, but that's not the most important thing.  The most important thing is the requirement focus and the way it drives you toward a more loosely coupled design.  But it's pretty fun to see that screen light up with green checks or circles after you've made a change.

The real value of the tests themselves is that today's unit tests become tomorrow's regression tests.  You won't remember why you wrote the code last week, but you don't have to.  The tests can also serve as mini-documentation of the requirements if you write the test names in a consistent way.  There are BDD frameworks that let you spit out some HTML to do exactly this.

Ruthless Refactoring

All this adds up to something of a safety net for refactoring. Think of the confidence you can have changing the code, even if it's another team members code.  Or if (gasp!) they make changes to your code.  It's nice to know your code still works.

If you write the tests first, you know you've got the "happy path" covered on your code.  That's the one you started with.  You can write a few more tests to cover the likely deviations from that happy path, but when you get to the weird stuff (what if you order $999,999,999,999 in widgets?), call it good and move on.

Tags: , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Why bother writing unit tests?

Posted by Joe Wilson on Friday, July 10, 2009 5:57 PM

More developers are coming around to the idea of writing unit tests for their code.  But not everyone is sold.  Let's take a look at the arguments for just writing code and either manually testing or handing the app over to QA:

  • It takes too long to write unit tests.
  • This is really simple code.  I could see maybe testing something complex, but this is just a CRUD app.
  • Write my tests before my code?  Are you kidding me?  I've always done it the other way and it's worked out just fine.

Let's take these one by one and see if they are still valid.

Time Argument

It doesn't take long to run the test.  I think there is consensus there.  But writing units tests and writing code is more lines of code than just writing the code.  More lines = more time, right?

Sure, as long as manual testing is less than the automated testing over time.  Not just the first time, but for the lifetime of that code.

If I'm using your old assembly from 2004 or web service from last month, I want some assurances it still works the way it was supposed to.  If you knew the secret way to manually test it, but have forgotten it, I don't fully trust your code.  I have to spend time going over that code and testing it out myself.

Code with a lifespan longer than your memory and availability to manually regression test it benefits from having a suite of unit tests that can be run in milliseconds.

Simplicity Argument

"If the code is really simple, there is no point in writing unit tests."

This is a good point.  Unit testing can help work through complex code, but what if the code is really easy?

I agree that there isn't a need to bother testing setting and getting properties and methods that just call other methods.  And please don't spend time testing code that is part of the .NET framework or an assembly you bought.  That's a waste of time.

But if you're using test-driven development, you start with the functionality, not the implementation.  So you write tests that cover the important/core stuff in your requirements.  You don't bother testing properties and call wrappers, which are just technical artifacts of the important stuff you are coding.  If it's the important stuff, shouldn't it be covered with unit tests?

TDD = My World Upside Down

Mad Hatter "Writing test before writing code is crazy talk." I also have had a great career without doing this, so what's the point?

I agree it is crazy.  I also agree it's a 180 on the conventional wisdom.  And I completely agree that it's harder than just writing the code.

But here's where I depart from the TDD crowd.  If you're new to unit testing, I just want you to write unit tests to cover the critical code for now.  It might lead you to TDD at some point, it might not.  But it's not an all or nothing thing.  Let's just try to work in as many tests as we can and leave it there for now. How many tests do you need?  What code coverage levels are you shooting for?  That's up to you.  If you feel good showing the code to your peers and are happy to be held accountable for the quality of your code, you're probably done.

I'll make the case for TDD another time.  Just baby steps for now.

Tags: ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Image Details