Test Driven Development Jottings
In my recent paddling I've been considering how best to introduce test driven development to some of my programs and how to overcome previously encountered problems. My only previous attempt at using test driven development in a serious way I abandoned as deadlines approached and rapid functionality changes were required. Additionally, I have tried to introduce some unit testing to smaller parts of applications and although the testing has not been entirely unsuccessful I've been left feeling that the testing code is unnecessary clutter within the sources, rather than an integral part of all of the work. The books that I have read (1)have offered much explanation about why unit testing is useful, a general approach to test-driven development and some of the technical details about using tools such as JUnit but they lack much insight into good test design techniques.
If you are an experienced test-driven developer who could offer me insight then your comments would be gratefully appreciated.
If you are a humble novice such as myself then I hope you find my experiences and ideas useful.
There are a number of reasons why I think that I have had problems:
1. Laziness
Quite simply I'd rather dive strait into coding rather than write out a set of test cases.
2. Time pressure
Writing unit tests offer medium and long term benefits (i.e. write tests now and benefit when I revisit the same code in the future) but there is no immediate benefit to writing a unit test. At work, when I'm looking to get the current task out of the way and move onto the next within a few hours there's no incentive to double the development time by writing tests first. Similarly, in my personal projects I normally struggle to find more than a couple of hours every few days, so I'm normally focused on getting some results out quick before I go to bed.
3. Programming style
I've always programmed in object oriented languages, using object oriented designs but I've never got into the habit of the truly extreme style of writing lots of small classes and short classes advocated in extreme programming. My programs tend be rather like a brick wall, with one layer of classes cemented on top of the previous - with the high level classes completely reliant on those in the lower layers. Thus my programs require really complex fixtures to be setup before the high level components can be tested.
4. Tests are coupled to the class interfaces that you design
In an early experiment with unit testing I wrote a series of tests for a single method - trying to find all of the cases that were likely to cause problems. The problem was that twice I had to change the parameters required by the method and, of course, both times I had to rewrite all of the tests. In hindsight there are several things that would have helped with this:
a) Design the interface better in the first place
b) Use method overloading and default parameters to eliminate the need to rewrite method calls when parameter lists are changed
c) Write a set of wrapper classes and methods within the testing code to shield the individual tests from minor changes to interfaces
I'm not entirely satisfied with any of these solutions. Trying to predict the future when designing interfaces in order to anticipate what new parameters may be required is a hopeless task, although it is reasonable to think through a method interface to the point where it is less likely that a change will be required.
I'm also not a big fan of using default parameters and method overloading simply to save rewriting calls to a method when the interface changes as these constructs often make code less readable.
Wrapper classes would solve the problem without adversely affecting the design of the existing code but seems like overkill for writing simple tests.
5. Tests can introduce unwanted constraints on the behavior of the program
With some tests that I have written I have found it hard to avoid making them excessively strict and I am concerned that slight changes will cause my tests to break when in fact the changes I have made were perfectly valid.
In particular at work I have written some a simple CAM add-on to RoofWright which exports DXF drawings. In order to do this I used a set of classes to model generic DXF drawings and wrote a series of DXF builder classes to map from my domain model to the required DXF. At the time I first wrote this there was no constraints on the exact layout of the drawing (the user could worry about that) so I didn't want to introduce constraints of this kind into the testing (i.e. by asserting that the coordinates of the DXF entities matched some exact values). If I ever had to introduce some way of laying out the drawings by translating all of my entities the tests would fail even though the change was perfectly reasonable.
In order to get around this I wrote some extra methods in my testing to allow me to ensure that check only that the generated drawing was a geometric translation of the original test data. This solution worked although it made the testing classes themselves more complex.
Perhaps at some point I'll manage to overcome these problems and become a better programmer!
Notes:
(1) I'm mainly referring to Kent Beck's books on XP, and Refactoring by Martin Fowler but I have also glanced at books about test driven development
If you are an experienced test-driven developer who could offer me insight then your comments would be gratefully appreciated.
If you are a humble novice such as myself then I hope you find my experiences and ideas useful.
There are a number of reasons why I think that I have had problems:
1. Laziness
Quite simply I'd rather dive strait into coding rather than write out a set of test cases.
2. Time pressure
Writing unit tests offer medium and long term benefits (i.e. write tests now and benefit when I revisit the same code in the future) but there is no immediate benefit to writing a unit test. At work, when I'm looking to get the current task out of the way and move onto the next within a few hours there's no incentive to double the development time by writing tests first. Similarly, in my personal projects I normally struggle to find more than a couple of hours every few days, so I'm normally focused on getting some results out quick before I go to bed.
3. Programming style
I've always programmed in object oriented languages, using object oriented designs but I've never got into the habit of the truly extreme style of writing lots of small classes and short classes advocated in extreme programming. My programs tend be rather like a brick wall, with one layer of classes cemented on top of the previous - with the high level classes completely reliant on those in the lower layers. Thus my programs require really complex fixtures to be setup before the high level components can be tested.
4. Tests are coupled to the class interfaces that you design
In an early experiment with unit testing I wrote a series of tests for a single method - trying to find all of the cases that were likely to cause problems. The problem was that twice I had to change the parameters required by the method and, of course, both times I had to rewrite all of the tests. In hindsight there are several things that would have helped with this:
a) Design the interface better in the first place
b) Use method overloading and default parameters to eliminate the need to rewrite method calls when parameter lists are changed
c) Write a set of wrapper classes and methods within the testing code to shield the individual tests from minor changes to interfaces
I'm not entirely satisfied with any of these solutions. Trying to predict the future when designing interfaces in order to anticipate what new parameters may be required is a hopeless task, although it is reasonable to think through a method interface to the point where it is less likely that a change will be required.
I'm also not a big fan of using default parameters and method overloading simply to save rewriting calls to a method when the interface changes as these constructs often make code less readable.
Wrapper classes would solve the problem without adversely affecting the design of the existing code but seems like overkill for writing simple tests.
5. Tests can introduce unwanted constraints on the behavior of the program
With some tests that I have written I have found it hard to avoid making them excessively strict and I am concerned that slight changes will cause my tests to break when in fact the changes I have made were perfectly valid.
In particular at work I have written some a simple CAM add-on to RoofWright which exports DXF drawings. In order to do this I used a set of classes to model generic DXF drawings and wrote a series of DXF builder classes to map from my domain model to the required DXF. At the time I first wrote this there was no constraints on the exact layout of the drawing (the user could worry about that) so I didn't want to introduce constraints of this kind into the testing (i.e. by asserting that the coordinates of the DXF entities matched some exact values). If I ever had to introduce some way of laying out the drawings by translating all of my entities the tests would fail even though the change was perfectly reasonable.
In order to get around this I wrote some extra methods in my testing to allow me to ensure that check only that the generated drawing was a geometric translation of the original test data. This solution worked although it made the testing classes themselves more complex.
Perhaps at some point I'll manage to overcome these problems and become a better programmer!
Notes:
(1) I'm mainly referring to Kent Beck's books on XP, and Refactoring by Martin Fowler but I have also glanced at books about test driven development

0 Comments:
Post a Comment
<< Home