Careful programmers test early and test often.
Unit testing. Isn't that some annoying requirement that we're going to ignore? What if I told you we are going to start with unit testing? You ask, "But how can I test code that hasn't been written?" I reply, "The unit test will define the code you are going to write."
Many developers get very nervous when you mention unit tests. I suspect they have this vision of a grand table with every single method listed, along with the expected results and pass/fail date. I'll leave that kind of unit testing to folks trying to get FDA approval. It's important, but not relevant in most programming projects.
The unit test will motivate the code that you write. In a sense, it is a little design document that says, "What will this bit of code do?" Or, in the language of object oriented programming, "What will these clusters of objects do?"
The crucial issue in constructing a unit test is scope. If the scope is too narrow, then the tests will be trivial and the objects might pass the tests, but there will be no design of their interactions. Certainly, interactions of objects are the crux of any object oriented design.
Likewise, if the scope is too broad, then there is a high chance that not every component of the new code will get tested. The programmer is then reduced to testing-by-poking-around, which is not an effective test strategy.
I like to start with a feature that is described in the requirements document and work down until a method doesn't need a unit test. That way, tests are grouped according to major feature and should include as many unit tests that deal with the feature in question as are necessary.
How do you know that a method doesn't need a unit test? First, can it be tested by inspection? If the code is simple enough that the developer can just look at it and verify its correctness then it is simple enough to not require a unit test. The developer should know when this is the case.
Unit tests will most likely be defined at the method level, so the art is to define the unit test on the methods that cannot be checked by inspection. Usually this is the case when the method involves a cluster of objects. Unit tests that isolate clusters of objects for testing are doubly useful, because they test for failures, and they also identify those segments of code that are related. People who revisit the code will use the unit tests to discover which objects are related, or which objects form a cluster. Hence: Unit tests isolate clusters of objects for future developers.
Another good litmus test is to look at the code and see if it throws an error or catches an error. If error handling is performed in a method, then that method can break. Generally, any method that can break is a good candidate for having a unit test, because it may break at some time, and then the unit test will be there to help you fix it.
The danger of not implementing a unit test on every method is that the coverage may be incomplete. Just because we don't test every method explicitly doesn't mean that methods can get away with not being tested. The programmer should know that their unit testing is complete when the unit tests cover at the very least the functional requirements of all the code. The careful programmer will know that their unit testing is complete when they have verified that their unit tests cover every cluster of objects that form their application.
Tim Burns is a consulting partner with Owl Mountain Software, LLC. http://www.owlmountain.com