Posted on May 21, 2017
by Doug Klugh

Does it really make sense to test code that hasn’t even been written yet?  Or to disrupt your development mojo every minute to stop, write tests, and refactor code?  It does if you want to deliver software faster, through better code, with fewer defects, and greater agility.  As software development leaders, it is important to understand that Test Driven Development goes well beyond quality control.

What is Test Driven Development?

As the name implies, Test Driven Development is an approach to software development through which a design and implementation are derived from writing tests.  It forces the developer to construct code that is loosely coupled, and therefore, easily testable.  The architecture and implementation evolve from the tests that are written prior to writing the code.  For those who have not yet adopted TDD, this often seems to be a very odd and unnatural way to develop software.  But as awkward as it may seem, it does work.

TDD was conceived by Kent Beck as an application of the test-first principle found in eXtreme Programming (also developed by Kent).  It advocated very short development cycles where a developer is continuously switching between writing a failing test that validates a requirement or design, writing implementation code to satisfy that test (make it pass), then refactoring the implementation and test code.  These short stages are often referred to as the Red, Green, Refactor stages of Test Driven Development.  Red indicates a failing test, green a passing test, then refactor.  It is important that the refactor stage include the clean up of the implementation code, along with the tests.

Uncle Bob Martin created the following three laws of test driven development:
  1. Write no production code except to pass a failing test
  2. Write only enough of a test to demonstrate a failure
  3. Write only enough production code to pass the test
By following these three laws, your system will naturally evolve into one that is easily testable.  This alone will greatly improve the quality of your code.

Enhancing Agility

It is no coincidence that Kent Beck, the father of XP and TDD, is also one of the 17 original signatories of the Agile Manifesto.  Agile was conceived with XP practices in mind; which is one of the reasons that XP and Agile practices blend together so well.  And TDD is no exception.  Like Agile, TDD is very much an iterative practice.  It facilitates the evolution of software over time.  As a result, this integration of practices fosters continuous improvement throughout the development lifecycle.

Breaking Bad

For those of you who have been programmers for a while, you probably have had the occasional experience of refactoring spaghetti code or code with a considerable amount of technical debt.  On rare occasions, leadership will support a dedicated effort to refactor code in hopes of increasing productivity.  But refactoring complex code (or even simple code that is poorly written) can be difficult, time-consuming, and risky.  When refactoring legacy code, we usually don’t have a good way of ensuring it continues to work properly.  We may even end up introducing bugs, which we discover through manual testing efforts.  Then we spend considerable time debugging the code, which eats into the time set aside for refactoring.  At the end of our time-boxed effort to improve the code, we discover we did not fix as much as we wanted or needed, since we spent so much time debugging.  Then as QA members begin running their suite of system and regression tests, they discover even more bugs that were not there before this whole effort began.  After a lot of debate and frustration, the decision is made to throw away the refactored code and revert to the original state.

Of course, this is not always the result of a big clean-up effort.  But I have worked in this industry long enough to see this play out on more than a few occasions.  And once you have had this experience, you will certainly shy away from participating in a big refactoring effort ever again.

This is obviously a big problem that occurs much more often than it should.  But we can’t afford to leave bad code lingering, which will only make us go slower as time passes.  We can’t be afraid to refactor code for fear of breaking it.

Eliminating Fear

So how do we eliminate the fear of refactoring?  By employing Test Driven Development.  TDD will result in a comprehensive suite of tests that will ensure that virtually all bugs are caught, can be easily automated and executed at the click of a button, can execute in a matter of minutes, will never get out of date with the system, and will eliminate the fear of changing the code.

Until they get experience with TDD, many developers and leaders think such a suite of tests is too time consuming to create.  The fact is they can develop faster, with fewer defects, spending less time debugging, and coding better.

TDD helps you to code better by producing testable code.  One of the biggest problems with software is that most of it is tightly coupled, making it very difficult to test.  By following TDD practices, you inevitably write loosely coupled code that is easily testable.  And not only does that produce testable code, it produces code that is extensible and easily maintained.

Building Code Coverage

It is well understood that if you want to optimize productivity, you must maximize the number of automated tests.  But all tests are not created equal.  While unit tests can be easily written, quickly executed, and clearly automated, system tests take much more effort to plan and write, and can require a considerable amount of time to execute.  Therefore, most of your tests should be unit tests.  They should execute in milliseconds and target 100% code coverage.  For these reasons, TDD is best achieved by writing unit tests.
Figure 1 - Test automation pyramid
The test automation pyramid, depicted above in figure 1, provides a testing strategy that effectively supports Agile development and DevOps models for continuous delivery.  This concept was introduced in 2009 by Mike Cohn in his book Succeeding with Agile and is often referenced by leaders such as Martin Fowler and Bob Martin.  It provides context as to how unit tests relate to other types of testing efforts.  This helps to ensure the right amount of testing within the various scopes of architecture while minimizing test overlap.
  Manual or adhoc tests are performed by people to explore the application for unexpected behavior, including User Acceptance Testing (UAT).  Since manual testing is the slowest and most expensive to perform, this type of testing is best kept to a minimum.
  System tests are written to validate all layers of the application beginning at the user interface.  This type of testing includes, but is not limited to, load testing and penetration testing.  Since these are expensive and time-consuming to write and execute, they usually target about 10% of the system.
  Integration tests are written to validate the linkage and communication between components.  This also helps to ensure a proper architecture is in place.  This should cover about 20% of the system.
  Component tests should then be written to validate the behavior of individual components; which encapsulate specific sets of business rules.  These should require little effort to write and execute quickly, since they are decoupled from the other application and system components.  Component tests should target about 50% of the system.
  Unit tests should form the foundation for all other tests and compose the majority of your automated test suites.  They can be written and executed easily and cheaply, early in the development lifecycle.

Conclusion

While Test Driven Development provides obvious value in supporting quality control, it’s significance goes well beyond ensuring that all defects are discovered and fixed prior to release.  TDD will give your team the courage to refactor any part of the code without fear of breaking it.  This enables your team to continuously improve the code base, without negatively impacting your release schedule.  The outcome is a loosely coupled code base that is easily maintainable, extensible, and yes, testable; resulting in shorter development time with fewer defects.
Tags:
Agile application development automated tests code defects quality risk TDD Xtreme Programming (XP)

Doug Klugh

Doug is an experienced software development leader, engineer, and craftsman having delivered consumer and enterprise firmware/software solutions servicing more than one billion users through 20+ years of leadership.

1 Comment

...

Lorenzo Hidalgo · May 25 2017

Great overview.  I would like to see an example starting with a unit test and building on it up through the pyramid.

Related Items


school Training

Principles & Practices of Test-Driven Development

This course teaches the principles and practices of Test-Driven Development (TDD) and demonstrates how proper software design evolves through application of the eXtreme Programming principle of Test First.  Unit testing principles are introduced, along with a thorough discussion on the benefits of TDD.  An application is developed (from start to finish) during this course to explain step-by-step and demonstrate first-hand how a high quality, testable design evolves by applying the three laws of Test-Driven Development.

Learn More schedule 3 Hours
computer Code Kata

Test-Driven Bowling

Any seasoned programmer who has tried doing Test-Driven Development (TDD) knows that it is a completely different paradigm of development from traditional methods.  So for most of us, it takes a lot of practice to become proficient at TDD.  This kata provides a simple exercise to practice the 3 basic laws of Test-Driven Development.

Get Started
computer Code Kata

Prime Transformation

A good TDD developer is one who can apply transformations well.  What are transformations?  They are the opposite of refactoring.  While refactoring is changing the structure of code without changing its behavior, transformations are changes to code that generalize behavior without changing its structure.  In this kata, you will create a function to calculate the prime factors of a given integer using TDD while applying transformations to pass the tests.

Get Started