In general I’m not really a fan of test driven development. Even in most cases I think unit tests are a waste of time. Too often they become a chore that you quickly bang out some basic tests, and then for the rest of their lifetime they are a pain to keep up to date. Here are some common examples I see far too often.
Testing language features: This is where you find tests that cover language features or are essentially testing third party software. For example testing data access by writing to a database table using your data access code, then reading back via straight SQL queries. Not only does this test very little of your code (you do have a test case for the data access library right?), but every time you change your class you have to update the query.
Solution: Test further up the code. Load and save an object and test that it remains the same. As a bonus your test case should remain valid even if you decide to change to storing XML.
Testing basic code: The general idea out there is that every bit of code needs its own test case. As an example imagine a container class to store normal data. This is simply a wrapper around a float and is really only there to provide contextual information for the developer. Creating a test case only for this class makes no sense because all you can possibly test is that you wrote the assignments the right way round.
Solution: In this case you need to consider where it is being used and why. As the normal wrapper is being used by a Mesh class to send and receive data to the GPU it makes more sense to create test cases for the Mesh class. This not only tests everything you would have tested in the basic normal test, but it actually allows you to check far more interesting cases, like making sure the stride length is correct.
Test driven development: Waterfall development model but with a unit test instead of a written document as your resulting artifact. On the one hand it makes you think about the interface of your code early on. Except for me this is often the biggest area of change during my development. Especially as I discover variations in performing tasks that end up changing a synchronous process to asynchronous.
Solution: Focus on getting the code correct and clean. At the end you’ll have a clear understanding of where the complicated aspects and interface boundaries exist. You’ll have a better understanding of what your code is doing and can focus testing in those areas. Never forget that writing code is about getting your desired result, not on making some ill conceived initial test case pass.
Testing atomic: Please make sure each test is as self contained as possible. File systems access should be sand boxed and should certainly not spit files all over your source tree! Test data should stay well clear of production code. Database tests should never point to a common database. Finally please flag obstructive unit tests so I can disable them when I want to do a test run, and continue to use my computer. If tests are broken up correctly you can even gain some benefit by running multiple tests in parallel. With something like cmake this is very easy to setup.
That ends my quick rant on unit testing. This is something of a precursor to another rant I’ll have latter about process in general, but what I prefer to focus on is making sure my code works. When tests are useful then by all means add them, but don’t ever just write a test because some process dictates you must!