Recently, I had a down and dirty discussion with a team who is gearing up for a new engagement. This team has been doing Scrum for a while but was struggling with how to fold testing more tightly into their development processes.
The heart of the matter ended up being differences in the purpose and perception of Unit Tests specifically. One camp saw the role of Unit Test as "to find bugs". That's it. They tell you where the bugs are. If they are just about finding bugs, they have more value to new developers than established developers. After all, great developers are much less likely to allow the kind of bugs found by Unit Tests in their code. As a great developer I'm going to catch bad parameters and logic flaws before checking in my code. Integration and functional bugs are probably not going to be found in Unit Tests anyway.
The other camp (read: in my opinion) was resolute that Unit Tests fill a much larger purpose. To my eyes, Units Tests go way beyond just finding bugs. Following are four big ways that Unit Tests can add significant value over and above just finding bugs.
Today's applications are more distributed and componentized than ever. The number of moving parts has just sky-rocketed with the use of frameworks and new API's, the prevalence of SOA and more component-oriented designs. Personally I see goodness here, but regardless if you like this direction or not, it's happening and is relevant to address. With this fragmentation comes a need to understand when the integration between components is complete. Simple Unit Tests can tell you quickly if the work being delegated downstream of a component is completed and ready. As you are bringing on multiple layers of components the pass rates of the Unit Tests at various layers can quickly show you the level of completeness for the entire integrated system.
One of the difficult parts of delivering a complete software product especially when the product is a framework or an API, if the product is extensible or supports programmability. In these cases specifically, Unit Tests can serve as great examples of how to utilize the API or exercise the programming model. They can often be directly consumed as documentation and sample code for SDKs or in knowledge transfers throughout the life of the product. Building this from outside the team without relying on the author of the component is extremely costly and difficult. However the returns are typically the most valuable.
Designing and building components is easy with the current toolsets. The downside of this is that we can create classes and objects so easily that we don't always think about the bigger picture. Taking time to write code that exercises a new component or interface can act as a sanity check to ensure that the component is easy to use and meets the programmability desires. It's quite often that I see a single API that requires two or three different programming or data interaction models. Some quick Unit Tests would have shown the designer how difficult and inconsistent the usage of the API had become.
As a great engineer, it might be totally natural for you to write code that has few bugs. Are the engineers who take up your legacy as experienced? Will the engineer who will open the code in six months to add some features or fix an integration issue be as comfortable knowing how to change things without breaking anything? Would you? Being able to run regressions over time adds a wonderful safety net in the identification of the impact of changes upstream, downstream, or inside your code. It doesn't mean you will (or can) catch everything with Unit Tests but it can sure give you a head start and a level of confidence.
So perhaps this is more about semantics or expectation setting, but I think it helps to keep in mind the variety of purposes Unit Testing can serve. When you expand the contributions made by Unit Tests hopefully you will have an incentive to get even your experienced engineers to spend time writing them.