Tuesday, 9 September 2008

TDD – Getting Started with Test-Driven Development

I have been expressing interest in getting up and running with TDD for quite some time, probably the best part of nearly two years in fact! (wow).

I have always struggled to get going though. In my previous job I was pretty much flying solo, and even though I am in a 6-man team in my new job, we still are pretty isolated (not my choice!).

Every time I have gone to the programming community and asked “how do I get started?” I pretty much got the same response wherever I went - “Just do it”. This was real frustrating because it can be so overwhelming, if you don’t know where to start, how can you start?

So I started to dig deeper for as many TDD/Automated Testing sites as I could, even if it was pages and pages of stuff that I didn’t want to know, if I got one little snippet of something useful, I would be happy.

I came across some great sites (links will follow). Some were very, very simple. Almost so simple that they don’t really cover the testing, they cover something much more important – YOU and YOUR MINDSET.

And this is it, the biggest problem was not writing and automated test, that’s just code, we can all code (OK well most of us at least try hard!). I have seen so many sites that use Debug.Assert or testing frameworks and cover those in great detail, but don’t actually approach the first major problem newcomers have, themselves.

So, here is my attempt at hitting on some of the key problems/questions I had when getting started.

I Don’t Know Where to Start?

  • Start afresh. Only think about writing tests when you are writing new code. This can be re-working of old code, or a completely new feature.
  • Start simple. Don’t go running off and trying to get your head round a testing framework as well as being TDD-esque. Debug.Assert works fine. Use it as a starting point. It doesn’t mess with your project or create dependencies.
  • Start positive. You are trying to improve your craft, feel good about it. I have seen plenty of developers out there that are happy to stagnate and not try new things to better themselves. You are doing the right thing, remember this and it will help stop you from giving up.
  • Start ready for a challenge. It is quite hard to start getting into testing. Expect a challenge, but remember – challenges can be overcome.

I Have Started. Now What?

So you are starting a new component/feature and you want to test the hell out of it, but you are looking at a blinking cursor and not sure what to do from here..

(these are not in any major order, but I have tried to keep it in line with the development “flow”)

Create a New Project to Contain the Testing Code

You don’t want your test code to get mixed up in your production code. The point here is that we are unit testing the code, so we should only ever be accessing it by its public interfaces. Create a new project within the solution. I normally just create a console app called “Test” and save it in the main application directory.

Always Have a Single Test Project for each Unit of Output

For example, a DLL has its own test application, a simple application has its own test application. However a large application should only have a test application for the main application element (since each referenced DLL will have its own test applications elsewhere).

Don’t Put Test Cases in the Main Program.cs File

It’s real easy to dive into the Main method and start writing Debug.Assert’s everywhere, but try not to do this, it can get messy real quick. Create class files for each feature set and then define a static method that the main method can call.

For example:

   1: static void Main(string[] args)
   2: {
   3:     BasicTests.Execute();
   4:     AdvancedTests.Execute();
   5: }

And then in each class, we can start adding our test cases to the Execute method:

   1: static void Execute()
   2: {
   3:     Debug.Assert(Some_Test_Here(), "Some_Test_Here Failed.");
   4:     Debug.Assert(Another_Test_Here(), "Another_Test_Here Failed.");
   5: }

This helps you keep your test cases organised, and the main method much cleaner.

Only Test For What You Expect

I had real problems when I first started because I was constantly sat there trying to figure out every possible problem that could occur and then trying to test for it and fix. This is a quick way to a headache. Testing should be a real YAGNI process. If you know there is a problem, then write a test for it. Otherwise, don’t bother.

Use Long, Descriptive Test Names

This is test code, no-one else (other than perhaps your dev team) will see it, ensure the names of your test cases describe exactly what the test is checking for.

Only Test One Thing

Each test case should only ever test one thing. If you ever find yourself putting “and” in the test case name, you’re doing something wrong.

Once a Bug has been Found, Prove It

Once an issue/bug has been found with some code (either through testing or actual use) before flying off to fix it, first prove it exists by creating a test that puts the code in the same conditions and then fails. You now have proof that:

  1. You have a bug to resolve.
  2. You haven’t solved it yet because the test is failing.

So once you have solved it, the test will pass and you can be confident you have corrected the issue.

This explorative movement can also lead to the discovery of deeper issues, which again, should all be proven by testing for, and then correcting.

Keep Tests Self-Contained

Each test should not rely on the outcome of another, nor need to be run in any specific order. This can be a real challenge when first starting, since it puts a lot of weight on your ability to refactor. This may mean a lot of code duplication to begin with, just DRY them up once you feel confident that you can do so. If not, then leave them “wet”, you’ll get there.

F5! F5! F5!

Run tests all the time. ANY code changes should result in a complete re-run of the full test suite. This includes changes to the test code and/or production code.

Be Efficient

Getting started with testing can and will be costly on the time it takes getting up and running, so remember to work efficiently. If you find yourself writing common code segments (Debug.Assert being a prime example) create code snippets for them! This really helps reduce some of the “cost”.

Where Next?

  • Keep writing more code, testing it as you go. As with all software development Quantity Trumps Quality.
  • There is no “one way”, there is just you and your code, come up with ways to make YOUR code more testable.
  • Look at ways other people are making their code more testable, such as Dependency Injection. Can you use it? Should you?
  • Perhaps look at testing frameworks to see if they can improve your testing process.

Summary

These are the most obvious points that I came across in my adventures. But like yourselves, I am still learning myself. So be sure to either add this feed to your RSS reader to keep track!

Here are some of the more prominent links that I have found useful:

I hope you find this post helpful!

Share:  digg it! del.icio.us Live Technorati Facebook

2 comments:

sean said...

Great post. I can only wonder if a good approach to this would be to WRITE down a test plan with all of the usual data [test case, expected result, actual result]

That would make it a little easier to go about this TDD wouldn't it? Thanks for turning me onto this.

sean808080

sean808080.com

Rob The Geek said...

Thanks for the comment Sean, and please accept my apologies for the delay in reply.

A very valid point, as with all of these new "things" we should always keep simplicity in our minds (reminds me of the Hipster PDA) :)

However, with the automated testing, while I find it can be sometimes helpful to draft up some plans for testing, doing the traditional table with expected results and the like can be very counter-productive.

We work in an industry where the cost of "measure twice cut once" isn't a real need at the testing stage, its probably quicker to delete a line of code than scribble out/erase a line of writing.

I totally agree a "battle plan" can be real useful, but the full table is kind of overkill and merely duplicating work (IMO).