Memory Lane

August 29, 2009 § Leave a comment

An article back from the grave.

Today I stumbled upon an old article I wrote on our team wiki titled “Project Proposal – A Generic Approach to TDD”.  I had leveraged the TDD formula in a couple earlier projects and this was an attempt at informing my current team how we could leverage it in our current project.  After re-reading it I figured there is some useful content worth posting in my blog even though my opinion on some things have changed since then.  I think it is good enough just the way it it is and does not need to be perfect.  So with a little obfuscation on project names, here it is…

 

Project Proposal – A Generic Approach to TDD

March 13, 2006

Description

The engineering team has tentatively agreed to utilize the Test Driven Development (TDD) methodology in an effort to ensure the quality & testability of the proposed ProjectB enhancements. Now that ProjectA is almost gold it is time for the team to start discussing how it is going to implement this methodology. Once the initial investigation is over, and focus shifts from research to design, it is important that we as a development team have a clear understanding of how we plan to accomplish TDD. In short, to facilitate success, we need to agree on the techniques & tools we will use.

Objectives and Benefits

This proposal is only meant to spur discussion, thought, & ideas about how we will administer TDD. The proposals’ purpose is to drive the team toward a unified understanding of what we will do, & how we will do it. With a collective understanding of TDD we will be successful in implementing it. Not everything that should be discussed is included in this proposal (e.g. test frame work, legacy code test integration, code coverage, error tests, test reviews, etc.).

Development Requirements

Here is my initial listing of our TDD requirements for ProjectB (to be discussed and expanded by the development team):

  1. New code needs to be testable via automated unit & integration tests.
  2. Old code needs to support automated unit & integration tests.
  3. Tests need to be run-able at any time, by anyone on the development team.
  4. Test results need to be stored in a repository.
  5. When a test fails, the appropriate people must be informed.
  6. We want to create a class structure which makes testing as easy as possible.

Definitions:

Unit test: Engineering test which occurs at the class/method level of code.
Integration test: Test which spans multiple classes/methods.

The Approach

When designing a TDD solution from scratch, integrating one in to existing code, or working with legacy code, the approach is the same:

  1. Write the test. Watch it fail.
  2. Write the code. Watch the test pass.
  3. Run the unit test suite. Watch it pass.
  4. Refactor. Repeat.

Since the team has not discussed and agreed upon a test frame work (e.g. Boost Unit Test Library), I will not get in to the implementation details of each step.  I would like to mainly focus on step 2 (Write the code).  To some degree, steps 1 (write the test) & 4 (Refactor) will also be touched on.  I can discuss these steps without knowledge of the implemented test suite.

When writing code it is important that we focus on simplicity. Simplicity leads towards understandability & maintainability of the system. We’ve all heard of the ‘KISS‘ principle and its usefulness. KISS becomes critical when automated testing is considered. A complex system yields complex tests, and hence a greater chance for defects to occur both in the code & in the tests.

In order to apply KISS, you need to know what ‘complex’ is. Two methods in a system which may implement similar behavior could be vastly different in complexity. In order for the team to use KISS and tackle complexity we need a way to measure code complexity. Cyclomatic complexity is a software metric designed to measure the general complexity of code. Its goal is to identify code which is ‘complex’ by measurement. For example, a method by default has a CC (Cyclomatic Complexity) of 1. If you add an ‘if’ statement to that method, add 1 to the methods CC. Adding an ‘else’ to the ‘if’ statement adds 1 to the CC. The method now has a CC measurement of 3, and is measurably more complex then a method which has a CC of 1.

Why is CC useful? It has two main benefits. One, it allows the development team to understand which code (new & legacy) is complex and subject to refactoring. Two, it identifies the number of base tests which are required. The CC index is a reflection of the number of code paths through a section of code. Since a properly designed test is supposed to test all paths through code, the CC index is an accurate reflection of the number of tests required to test that code.

Since simplicity leads to maintainability and a reduction in defects, a CC index will allow the team to identify code which is not simple and flag it for refactoring (step 4 above).

Now that we have a technique for identifying complex systems (the ‘what’), the next step is ‘how’. How do we create simple software systems (i.e. Low CC code)? The answer is to create code in which methods and classes are isolated and loosely depended on each other. Following patterns like Strategy & Factory Method will act as a guide.

For example, let’s assume that you have a small method with a CC of 5. This would require 5 unit tests to correctly test the code paths through the method. Using the Factory Method Pattern, the method can be refactored to a CC of 1 and 4 additional classes or methods have been created, which are now isolated, require smaller tests (each have a CC of 1), are easier to understand, and are less defect prone. Applying the Factory Method Pattern to a method with a CC of 5 does not make the benefits apparent, but anyone who has worked on ProjectA code has seen methods which would have CC indexes of 25+. In cases like these, the Factory Method Pattern would make sense. If it’s easier to test, it’s easier to identify and resolve defects.

Conclusion

What I’ve done above is outlined a basic approach to identify and reduce complex code for the purpose of testing. With up to 4 different engineers working in the ProjectB code, simple, loosely coupled code will be a team performance advantage.

Summary

  1. Focus on KISS principle.
  2. Use Cyclomatic complexity to identify complex code for potential refactoring, & the number of tests required.
  3. Create/Refactor in to simple, isolated, fine-grained code using Strategy & Factory Method patterns.

As I stated before, these are few ideas I wanted to use to spur discussions. In my opinion, it would be a good idea to have a TDD plan in place before we start creating engineering designs, which will likely happen next month.

Please provide me with your comments, suggestions, & ideas via email or in person (preferred). I would like to start these discussions in parallel with the creation of our use cases.

Leave a comment

What’s this?

You are currently reading Memory Lane at Journeyman.

meta