Design Principles in Test First Programming
By: Erik Meade
Introduction
The purpose of this article is to examine how test first programming produces code which adheres to certain design
principles. In particular we will see that test first programming insures that we follow the Open/Closed,LiskovSubstitution Principle
and Dependency Inversion principles, introduced to us in 1996 by Robert C. Martin. Here we will revisit the Copy program, which Robert used to demonstrate the Dependency Inversion Principle, only we will do
so test first using Kent Beck and Erich Gamma's JUnit , a testing framework for Java.
The Copy program
Robert used the Copy program as an example of code rot. The basic Copy program reads from the keyboard and prints
to the printer. Enhancing the Copy program so it can also print to the disk results in an if else statement based on type
which definitely has a code smell to those of us who understand the Open/Closed Principle. As a reminder the
Open/Closed Principle states that we should add new functionality by adding new code, not by editing old code.
Since the entire purpose of the Copy program is to copy, We will focus on writing the test for it first, but how do we
write a test for a class that uses two other classes without creating those classes first? Do the simplest thing that could
possibly work, create mock objects for the Reader and Writer classes. Now we have enough to implement test first. The
MockReader will allow us to hard code what we are going to read, and the MockWriter will expose a field so we can
check that it "wrote" the right thing.
import junit.framework.TestCase;
public class CopyTest extends TestCase {
public CopyTest ( String name ) {
super ( name );
}
public void testCopy () {
String testLine = "input";
MockReader reader = new MockReader ( testLine );
MockWriter writer = new MockWriter ();
Copy copy = new Copy ( reader, writer );
copy.copy ();
assertEquals ( testLine , writer.output );
}
}
Compiling this produces a few errors, all related to the fact that none of the classes used exist. Addressing each error in
the order they appear results in the following:
class MockReader {
String input;
MockReader ( String input ) {
this.input = input;
}
public String readln () {
String line = input;
input = null;
return line;
}
}
class MockWriter {
String output;
public void writeln ( String line ) {
output = line;
}
}
class Copy {
MockReader reader;
MockWriter writer;
Copy ( MockReader reader , MockWriter writer ) {
this.reader = reader;
this.writer = writer;
}
void copy () {
String line;
while ( ( line = reader.readln () ) != null )
writer.writeln ( line )
Full article...
Other Resource
... to read more articles, visit http://sqa.fyicenter.com/art/
|