We all know that exercise is good for us, members of the species Homo Sapiens Sitonourasses. And yet most of us don’t do enough of it. The same is true for Test-Driven Development (TDD). But the similarity doesn’t end there.
With regular exercise, you can grow your muscles. The way this works is not linear, however. It follows an upward saw-tooth pattern: first you damage your muscles (training), then they heal (recovery) and eventually grow stronger than before (supercompensation), then you damage them again, etc:
TDD follows the Damage-Heal-Grow cycle as well.
First we damage the system by writing a test that fails. Where before we could gloat in knowing all was well because our test suite said so, now we have to admit that there is still something wrong with our code. For some, this realization may hurt as much as their sore muscles after working out.
Luckily, we heal the system quickly by writing only the minimal amount of code required to make the test pass. With everything back to green in minutes or even seconds, we have every right to feel good again. TDD is perfect for short-attention spans.
Finally we grow the system by improving the design. The system can now handle everything we’ve ever thrown at it and more, because we generalized concepts and gave them a proper place in the code.
Damaging and healing happen on two levels in TDD.
First there is the syntactic level. You write a test that calls code that doesn’t exist yet, so the code doesn’t even compile. The healing that follows is to make the code compile, even though it doesn’t yet pass the test.
Only after this syntactic healing do you change the code to pass the test. The latter is more of a semantic type of healing.
The distinction between syntactic and semantic healing has implications for how we work.
There are only so many ways that a program can be syntactically broken, and in many cases, a sophisticated enough IDE can help heal it. For example, when you write a test that refers to a class that doesn’t yet exist, Eclipse offers a Quick Fix to create the class for you.
Semantic healing, on the other hand, is more difficult. The transformations of the Transformation Priority Premise can be seen as standard building blocks, and at least some of them can be automated. But that’s still a long way from the IDE generating the code that will make the failing test pass.
I haven’t seen many TDD practitioners do the equivalent of walking around the beach showing off their rock-hard abs, and that’s probably a good thing.
But just as we appreciate how a strong, muscular friend can easily handle any piece of furniture when he helps us move, so do product owners like it that we can always deliver any feature in a short amount of time.
Unfortunately, it just doesn’t score us any dates; for that we really do need to hit the gym.
After only a couple of weeks of Judo practice, my son got bored. He complained that he wasn’t learning anything, because he kept doing the same thing over and over.
It’s not just young children that confuse learning and doing new things. For instance, how many software developers go through the trouble of deliberate practice by performing katas or attending dojos?
It may seem silly to repeat exercises that you’ve already done many times, but it’s not. It’s the only way to become a black belt in your field. And remember that mastery is one of the three intrinsic motivators (the others being autonomy and purpose).
Practicing means slowing down and moving focus from outcome to process. It’s best to use simple exercises that you can complete in a limited amount of time, so you can do the same exercise multiple times.
I’ve found that I virtually always learn something new when I practice. That’s not because I’ve forgotten how to solve the problem since last time, but because I’ve learned new things since then and thus see the world through new eyes.
For example, since Java 8 came out I’ve been trying to use the new stream classes to help move to a more functional style of programming. This has changed the way I look at old problems, like FizzBuzz.
Let’s see this in action. Of course, I start by adding a test:
+ package remonsinnema.blog.fizzbuzz;
+
+ import static org.junit.Assert.assertEquals;
+
+ import org.junit.Test;
+
+
+ public class WhenFizzingAndBuzzing {
+
+ private final FizzBuzz fizzbuzz = new FizzBuzz();
+
+ @Test
+ public void shouldReplaceWithFizzAndBuzz() {
+ assertEquals(“1”, “1”, fizzbuzz.get(1));
+ }
+
+ }
This test uses the When…Should form of unit testing that helps focus on behavior rather than implementation details. I let Eclipse generate the code required to make this compile:
+ package remonsinnema.blog.fizzbuzz;
+
+
+ public class FizzBuzz {
+
+ public String get(int i) {
+ return null;
+ }
+
+ }
The simplest code that makes the test pass is to fake it:
package remonsinnema.blog.fizzbuzz;
public class FizzBuzz {
public String get(int i) {
– return null;
+ return “1”;
}
}
Now that the test passes, it’s time for refactoring. I remove duplication from the test:
public class WhenFizzingAndBuzzing {
@Test
public void shouldReplaceWithFizzAndBuzz() {
– assertEquals(“1”, “1”, fizzbuzz.get(1));
+ assertFizzBuzz(“1”, 1);
+ }
+
+ private void assertFizzBuzz(String expected, int n) {
Next I add a test to force the real implementation:
public class WhenFizzingAndBuzzing {
@Test
public void shouldReplaceWithFizzAndBuzz() {
assertFizzBuzz(“1”, 1);
+ assertFizzBuzz(“2”, 2);
}
private void assertFizzBuzz(String expected, int n) {
package remonsinnema.blog.fizzbuzz;
public class FizzBuzz {
– public String get(int i) {
– return “1”;
+ public String get(int n) {
+ return Integer.toString(n);
}
}
OK, now let’s get real with a test for Fizz:
public class WhenFizzingAndBuzzing {
public void shouldReplaceWithFizzAndBuzz() {
assertFizzBuzz(“1”, 1);
assertFizzBuzz(“2”, 2);
+ assertFizzBuzz(“Fizz”, 3);
}
private void assertFizzBuzz(String expected, int n) {
package remonsinnema.blog.fizzbuzz;
public class FizzBuzz {
public String get(int n) {
+ if (n == 3) {
+ return “Fizz”;
+ }
return Integer.toString(n);
}
Similar for Buzz:
public class WhenFizzingAndBuzzing {
assertFizzBuzz(“Fizz”, 3);
+ assertFizzBuzz(“4”, 4);
+ assertFizzBuzz(“Buzz”, 5);
}
private void assertFizzBuzz(String expected, int n) {
public class FizzBuzz {
if (n == 3) {
return “Fizz”;
}
+ if (n == 5) {
+ return “Buzz”;
+ }
return Integer.toString(n);
}
Here I just copied and pasted the if statement to get it working quickly. We shouldn’t stop there, of course, but get rid of the dirty stuff. In this case, that’s duplication.
First, let’s update the code to make the duplication more apparent:
package remonsinnema.blog.fizzbuzz;
public class FizzBuzz {
public String get(int n) {
– if (n == 3) {
– return “Fizz”;
+ MultipleReplacer replacer = new MultipleReplacer(3, “Fizz”);
+ if (n == replacer.getValue()) {
+ return replacer.getText();
}
– if (n == 5) {
– return “Buzz”;
+ replacer = new MultipleReplacer(5, “Buzz”);
+ if (n == replacer.getValue()) {
+ return replacer.getText();
}
return Integer.toString(n);
}
+ package remonsinnema.blog.fizzbuzz;
+
+
+ public class MultipleReplacer {
+
+ private final int value;
+ private final String text;
+
+ public MultipleReplacer(int value, String text) {
+ this.value = value;
+ this.text = text;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ }
I just created a new value object to hold the two values that I had to change after the copy/paste.
Now that the duplication is clearer, it’s easy to remove:
package remonsinnema.blog.fizzbuzz;
+ import java.util.Arrays;
+ import java.util.Collection;
+
public class FizzBuzz {
+ private final Collection<MultipleReplacer> replacers = Arrays.asList(
+ new MultipleReplacer(3, “Fizz”), new MultipleReplacer(5, “Buzz”));
+
public String get(int n) {
– MultipleReplacer replacer = new MultipleReplacer(3, “Fizz”);
– if (n == replacer.getValue()) {
– return replacer.getText();
– }
– replacer = new MultipleReplacer(5, “Buzz”);
– if (n == replacer.getValue()) {
– return replacer.getText();
+ for (MultipleReplacer replacer : replacers) {
+ if (n == replacer.getValue()) {
+ return replacer.getText();
+ }
}
return Integer.toString(n);
}
I’m not done cleaning up, however. The current code suffers from feature envy, which I resolve by moving behavior into the value object:
package remonsinnema.blog.fizzbuzz;
import java.util.Arrays;
import java.util.Collection;
+ import java.util.Optional;
public class FizzBuzz {
public String get(int n) {
for (MultipleReplacer replacer : replacers) {
– if (n == replacer.getValue()) {
– return replacer.getText();
+ Optional<String> result = replacer.textFor(n);
+ if (result.isPresent()) {
+ return result.get();
}
}
return Integer.toString(n);
package remonsinnema.blog.fizzbuzz;
+ import java.util.Optional;
+
public class MultipleReplacer {
this.text = text;
}
– public int getValue() {
– return value;
– }
–
– public String getText() {
– return text;
+ public Optional<String> textFor(int n) {
+ if (n == value) {
+ return Optional.of(text);
+ }
+ return Optional.empty();
}
}
Now that I’m done refactoring, I can continue with multiples:
public class WhenFizzingAndBuzzing {
assertFizzBuzz(“Fizz”, 3);
assertFizzBuzz(“4”, 4);
assertFizzBuzz(“Buzz”, 5);
+ assertFizzBuzz(“Fizz”, 6);
}
private void assertFizzBuzz(String expected, int n) {
public class MultipleReplacer {
}
public Optional<String> textFor(int n) {
– if (n == value) {
+ if (n % value == 0) {
return Optional.of(text);
}
return Optional.empty();
The final test is for simultaneous “Fizz” and “Buzz”:
public class WhenFizzingAndBuzzing {
assertFizzBuzz(“4”, 4);
assertFizzBuzz(“Buzz”, 5);
assertFizzBuzz(“Fizz”, 6);
+ assertFizzBuzz(“7”, 7);
+ assertFizzBuzz(“8”, 8);
+ assertFizzBuzz(“Fizz”, 9);
+ assertFizzBuzz(“Buzz”, 10);
+ assertFizzBuzz(“11”, 11);
+ assertFizzBuzz(“Fizz”, 12);
+ assertFizzBuzz(“13”, 13);
+ assertFizzBuzz(“14”, 14);
+ assertFizzBuzz(“FizzBuzz”, 15);
}
private void assertFizzBuzz(String expected, int n) {
public class FizzBuzz {
new MultipleReplacer(3, “Fizz”), new MultipleReplacer(5, “Buzz”));
Now, what is responsible behavior in this context?
It’s many things. It’s delivering software that solves real needs, that works reliably, is secure, is a pleasure to use, etc. etc.
There is one constant in all these aspects: they change. Business needs evolve. New security threats emerge. New usability patterns come into fashion. New technology is introduced at breakneck speed.
The number one thing a software professional must do is to form an attitude of embracing change. We cope with change by writing programs that are easy to change.
Adaptability is not something we’ll explicitly see in the requirements; it’s silently assumed. We must nevertheless take our responsibility to bake it in.
Unfortunately, adaptability doesn’t find its way into our programs by accident. Writing programs that are easy to change is not easy but requires a considerable amount of effort and skill. The skill of a craftsman.
2. Hone Your Skills
How do we acquire the required skills to keep our programs adaptable?
We need to learn. And the more we learn, the more we’ll find that there’s always more to learn. That should make us humble.
How do we learn?
By reading/watching/listening, by practicing, and by doing. We need to read a lot and go to conferences to infuse our minds with fresh ideas. We need to practice to put such new ideas to the test in a safe environment. Finally, we need to incorporate those ideas into our daily practices to actually profit from them.
BTW, I don’t agree with the statement in the article that
Programmers cannot improve their skills by doing the same exercise repeatedly.
One part of mastering a skill is building muscle memory, and that’s what katas like Roman Numerals are for. Athletes and musicians understand that all too well.
Nowadays software development is mostly a team sport, because we’ve pushed our programs to the point where they’re too big to fail build alone. We are part of a larger community and the craftsmanship model emphasizes that.
There are both pros and cons to being part of a community. On the bright side, there are many people around us who share our interests and are willing to help us out, for instance in code retreats. The flip side is that we need to learn soft skills, like how to influence others or how to work in a team.
To make matters even more interesting, we’re actually simultaneously part of multiple communities: our immediate team, our industry (e.g. healthcare), and our community of interest (e.g. software security or REST), to name a few. We should participate in each, understanding that each of those communities will have their own culture.
It’s All About the Journey
Software craftsmanship is not about becoming a master and then resting on your laurels.
While we should aspire to master all aspects of software development, we can’t hope to actually achieve it. It’s more about the journey than the destination. And about the fun we can have along the way.
I’m lucky to work for a company that makes it possible to have a good technical career, so that I don’t have to become a manager just to get more money.
I like being an individual contributor because it gives me a sense of accomplishment when I get stuff done myself.
Anyway, I believe the distinction between manager and individual contributor is useful when writing code as well.
At code retreats, you’ll find people who start working on the problem from the bottom (cell) and those who start at the top (universe, world, grid, game, or whatever they call it).
Interestingly, both groups tend to run into a similar problem.
Starting at the bottom makes it easy to quickly implement the rules in a cell. But then the problem becomes how to connect the cells into a universe.
This often looks like too big of a step to properly test, e.g. with a new green test every two minutes.
Starting at the top makes sure we are not doing anything unnecessary or building any parts that would not connect properly. But here too it’s difficult to keep chipping away in baby steps.
The Single Responsibility Principle
To evolve a whole universe, at least these things need to happen:
Iterate over all cells
Determine each cell’s neighbors
Count the number of alive neighbors
Determine the cell’s next state based on its current state and the number of alive neighbors
Depending on some implementation choices (like keeping the universe immutable), there may be more work.
Yet the Single Responsibility Principle tells us that a class should have only one reason to change. You may count items #2 and #3 as one responsibility (or #3 and #4), but clearly there is more than one involved.
Manager Classes and Individual Contributor Classes and How to Test Them
Now remember the manager/individual contributor (IC) distinction. Managers manage ICs; that’s their whole job. We should count managing other classes as a responsibility too.
That would mean that a class either implements some logic (IC), or coordinates with other classes (manager), but not both.
That would explain why both code retreat groups seem to get stuck: they have to switch from state-based to interaction-based testing and it seems that many developers don’t have a lot of experience with the latter.
Test-Driving Manager Classes
So let’s try interaction-based testing of a manager class. I’ll keep with the example of the Game of Life.
I’d like to begin with an architectural choice: I want the universe to be immutable, so that it would be easy to introduce multi-threading later. As the game evolves, I’ll have to create a new universe for every generation:
public class GameTest {
@Test
public void clonesImmutableUniverseWhenEvolving() {
Universe universe = mock(Universe.class, "old");
Universe nextGeneration = mock(Universe.class, "new");
when(universe.clone()).thenReturn(nextGeneration);
Game game = new Game(universe);
assertSame("Initial", universe, game.universe());
game.evolve();
assertSame("Next generation", nextGeneration,
game.universe());
}
}
I’m using Mockito to define how the Game interacts with the Universe. Mocking works best with interfaces, so Universe is an interface.
That means the game doesn’t know the type of universe and therefore can’t create a new instance of it. The universe provides a factory methodclone() for this purpose.
If the universe is to be immutable, then its state should be provided in its constructor. This constructor is called by clone(), so that method needs the new state as its input.
What does that state look like? At this point we don’t know anything about the universe other than that it contains cells. Let’s keep it simple and provide the cells as input:
public class GameTest {
@SuppressWarnings("unchecked")
@Test
public void clonesImmutableUniverseWhenEvolving() {
Universe universe = mock(Universe.class, "old");
Universe nextGeneration = mock(Universe.class, "new");
when(universe.clone(any(Iterable.class)))
.thenReturn(nextGeneration);
Game game = new Game(universe);
assertSame("Initial", universe, game.universe());
game.evolve();
assertSame("Next generation", nextGeneration,
game.universe());
}
}
OK, so who is going to supply those cells to clone()? The game is a manager, so it should only manage, not provide logic. The universe is an IC that is responsible for the cells and their interconnections, so it already has a responsibility.
So it seems like we need a new type. The new type should be managed by the game, since that’s the only manager class we have. Thus we should add a new test in GameTest for this interaction.
The new type should be responsible for determining the new state of the universe from the old one. This is where the game’s rules come into play, so let’s call the new type Rules.
Let’s start with testing that the game manages the rules:
public class GameTest {
@Test
public void consultsRulesWhenEvolving() {
Rules rules = mock(Rules.class);
Game game = new Game(null, rules);
assertSame("Rules", rules, game.rules());
}
}
Now we want to test that evolve() consults the rules. Since the game is a manager, it can only request output from a collaborator and provide that as input to another collaborator. It shouldn’t contain any logic itself, since that would make it an IC.
The only other collaborator that the game currently has is the universe and that seems the correct candidate to provide the cells. But we need more than the cells to implement the rules: we also need to number of alive neighbors of each cell.
The universe is clearly the correct place to determine a cell’s neighbors. Should it also count the number of alive ones?
I guess it could, but I don’t particularly like that: the rules for Life use the number of alive neighbors, but it’s not hard to imagine a different set of rules that also depend on the number of dead neighbors. So I feel that the rules should do the counting.
This means that the input to the rules is a cell and its neighbors. Since Java doesn’t allow returning two pieces of information, we need to combine them. Let’s call the combination a neighborhood:
public class NeighborhoodTest {
@Test
public void holdsACellAndItsNeighbors() {
Cell cell = mock(Cell.class, "cell");
List<Cell> neighbors = Arrays.asList(
mock(Cell.class, "neighbor1"),
mock(Cell.class, "neighbor2"));
Neighborhood neighborhood = new Neighborhood(cell,
neighbors);
assertSame("Cell", cell, neighborhood.cell());
assertEquals("Neighbors", neighbors,
neighborhood.neighbors());
}
}
Now we can make the universe return a neighborhood for each of the cells it contains and verify that those neighborhoods are used as input for the rules:
public class GameTest {
@SuppressWarnings("unchecked")
@Test
public void clonesImmutableUniverseWhenEvolving() {
Universe universe = mock(Universe.class, "old");
Universe nextGeneration = mock(Universe.class, "new");
when(universe.clone(any(Iterable.class)))
.thenReturn(nextGeneration);
when(universe.neighborhoods()).thenReturn(
new ArrayList<Neighborhood>());
Game game = new Game(universe, mock(Rules.class));
assertSame("Initial", universe, game.universe());
game.evolve();
assertSame("Next generation", nextGeneration,
game.universe());
}
@Test
public void consultsRulesWhenEvolving() {
Universe universe = mock(Universe.class);
Neighborhood neighborhood1 = new Neighborhood(
mock(Cell.class),
Arrays.asList(mock(Cell.class)));
Neighborhood neighborhood2 = new Neighborhood(
mock(Cell.class),
Arrays.asList(mock(Cell.class)));
when(universe.neighborhoods()).thenReturn(
Arrays.asList(neighborhood1, neighborhood2));
Rules rules = mock(Rules.class);
Game game = new Game(universe, rules);
assertSame("Rules", rules, game.rules());
game.evolve();
verify(rules).nextGeneration(neighborhood1);
verify(rules).nextGeneration(neighborhood2);
}
}
The next step is to make sure that the output from the rules is used to construct the new universe:
public class GameTest {
@Test
public void consultsRulesWhenEvolving() {
Universe universe = mock(Universe.class);
Neighborhood neighborhood1 = new Neighborhood(
mock(Cell.class),
Arrays.asList(mock(Cell.class)));
Neighborhood neighborhood2 = new Neighborhood(
mock(Cell.class),
Arrays.asList(mock(Cell.class)));
when(universe.neighborhoods()).thenReturn(
Arrays.asList(neighborhood1, neighborhood2));
Rules rules = mock(Rules.class);
Cell cell1 = mock(Cell.class, "cell1");
Cell cell2 = mock(Cell.class, "cell2");
when(rules.nextGeneration(neighborhood1))
.thenReturn(cell1);
when(rules.nextGeneration(neighborhood2))
.thenReturn(cell2);
Game game = new Game(universe, rules);
assertSame("Rules", rules, game.rules());
game.evolve();
verify(universe).clone(eq(
Arrays.asList(cell1, cell2)));
}
}
At this point we’re done with the Game class. All that is left to do, is create implementations for the three interfaces we introduced: Universe, Cell, and Rules. Each of these is an IC class, and thus fairly straightforward to test-drive using state-based testing.
Conclusion
I find that the distinction between manager and individual contributor classes helps me in deciding what the next test should be.
Server software needs to run unsupervised for long periods of time to be practical.
Release It! is full of horror stories about programming mistakes that get in the way of that lofty goal.
One example is opening files and forgetting to close them.
On some operating systems this will eventually lead to a Too many open files error when the number of open files passes a certain limit.
Of course we want to make sure that doesn’t happen to us. IDEs like Eclipse can give you warnings in certain cases to help with that, but if you’re test infected like me, you will want to write a test to make sure.
This is one of those situations where people less committed to (unit) testing tend to give up. There is nothing in the Java file system API that tells you how many files are open, so it simply cannot be done, right?
Second, we can do even better if we step back for a moment and take a look at the bigger picture. Chances are that our application isn’t really about files; that using files is simply a convenient implementation choice.
In the lingo of Eric Evans’ classic Domain-Driven Design, we have (at least) two bounded contexts: your core domain (what people buy/use your application for) and the file system.
A bounded context delimits the applicability of a particular model so that team members have a clear and shared understanding of what has to be consistent and how it relates to other contexts.
Evans describes a number of strategies for dealing with different bounded contexts. In our example, the file system API is not under our control, which rules out the majority of them. The remaining strategies are:
Separate Ways, i.e. use something other than the file system. That probably doesn’t make sense in this example
Conformist, i.e. follow the Java model slavishly. This is what most developers do without giving it much thought
Anti-Corruption Layer, i.e. create an isolating layer to provide clients with functionality in terms of their own domain model. This layer translates in both directions as necessary between the two models
This last strategy gives us more than “just” the opportunity to keep our models clear and to the point. By introducing interfaces that we control in our anti-corruption layer, we also gain the opportunity to mock those interfaces in our tests, making it very easy to verify that we indeed close all the files we open.
This is yet another example where difficulty in unit testing a piece of code points to an opportunity to improve the design. If we consistently act on such opportunities, we will end up with a clean architecture that is a joy to work with.
This left me with nothing for unit tests. So I installed the JavaScript tools from Eclipse. That gave me some JS support, but nothing for creating unit tests.
Some googling told me there is such a thing as JsUnit, the JS port of my beloved JUnit. Unfortunately it doesn’t seem to come with Eclipse support, even though this thread indicates it does (or did).
Maybe I’m just doing it wrong. I’d appreciate any hints in the comments.
Now that I’m all set up, it’s time to do a little exercise to get my feet wet. For this I picked the Roman Numerals kata.
I started out by following this JsTestDriver example. I created a new JavaScript project in Eclipse, added src/main/js and src/test/js folders, and created the JsTestDriver configuration file:
Next, I opened the JsTestDriver window using Window|Show View|Other|JavaScript|JsTestDriver and started the JsTestDriver server. I then opened the client in FireFox at http://127.0.0.1:42442/capture.
The next step was to create a new run configuration: Run|Run Configurations|JsTestDriver Test. I selected the project and the JsTestDriver configuration within the project, and checked Run on Every Save.
Now everything is set up to start the TDD cycle. First a test:
RomanNumeralsTest = TestCase("RomanNumeralsTest");
RomanNumeralsTest.prototype.testArabicToRoman
= function() {
var romanNumerals = new TestApp.RomanNumerals();
assertEquals("i", romanNumerals.arabicToRoman(1));
};
The cool thing about JsTestDriver is that it automatically runs all the tests every time you change something. This shortens the feedback cycle and keeps you in the flow. For Java, InfiniTest does the same.
The problem with my current tool chain is that support for renaming is extremely limited. I got Operation unavailable on the current selection. Select a JavaScript project, source folder, resource, or a JavaScript file, or a non-readonly type, var, function, parameter, local variable, or type variable.
Other refactorings do exist, like Extract Local Variable and Extract Method, but they mess up the formatting. They also give errors, but then work when trying again.
All in all I feel satisfied with the first steps I’ve taken on this journey. I’m a little worried about the stability of the tools. I also realize I have a more to learn about JavaScript prototypes.
One of the challenges of maintaining a consistent programming style in a team is for everyone to have the same workspace settings, especially in the area of compiler warnings.
Every time a new member joins the team, an existing member sets up a new environment, or a new version of the compiler comes along, you havebook.e to synchronize settings.
My team recently started using Workspace Mechanic, an Eclipse plug-in that allows you to save those settings in an XML file that you put under source control.
The plug-in periodically compares the workspace settings with the contents of that file. It notifies you in case of differences, and allows you to update your environment with a couple of clicks.
Towards a Frictionless Development Environment
Workspace Mechanic is a good example of a lubricant, a tool that lubricates the development process to reduce friction.
My ideal is to take this to the extreme with a Frictionless Development Environment (FDE) in which all software development activities go very smoothly.
Let’s see what we would likely need to make such an FDE a reality.
In this post, I will look at a very small example that uncovers some of the basic components of an FDE.
It would be nicer if the IDE would understand what you’re trying to do and automatically create the skeleton for the class under test for you and save it in the right place.
The crux is for the tool to understand what you are doing, or else it could easily draw the wrong conclusion and create all kinds of artifacts that you don’t want.
This kind of knowledge is highly user and potentially even project specific. It is therefore imperative that the tool collects usage data and uses that to optimize its assistance. We’re likely talking about big data here.
Given the fact that it’s expensive in terms of storage and computing power to collect and analyze these statistics, it makes sense to do this in a cloud environment.
That will also allow for quicker learning of usage patterns when working on different machines, like in the office and at home. More importantly, it allows building on usage patterns of other people.
What this example also shows, is that we’ll need many small, very focused lubricants. This makes it unlikely for one organization to provide all lubricants for an FDE that suits everybody, even for a specific language.
The only practical way of assembling an FDE is through a plug-in architecture for lubricants.
Building an FDE will be a huge effort. To realize it on the short term, we’ll probably need an open source model. No one company could put in the resource required to pull this off in even a couple of years.
The Essential Components of a Frictionless Development Environment
This small example uncovered the following building blocks for a Frictionless Development Environment:
Cloud Computing will provide economies of scale and access from anywhere
Big Data Analytics will discern usage patterns
Recommendation Engines will convert usage patterns into context-aware lubricants
A Plug-in architecture will allow different parties to contribute lubricants and usage analysis tools
An Open Source model will allow many organizations and individuals to collaborate
What do you think?
Do you agree with the proposed components of an FDE? Did I miss something?
Refactorings are standard alterations of the code that change its internal structure without changing its external behavior.
Now, if the Green and Refactor phases are each others opposite, then you might think that there are “opposite refactorings” as well. You would be right.
Robert Martin‘s transformations are standard alterations of the code that change its external behavior without changing its internal structure.
Automated Transformations?
Most of us use powerful IDEs to write our code. These IDEs support refactorings, which means that they can do the code alteration for you in a manner that is guaranteed to be safe.
So do we need something similar for transformations? I think not.
Some transformations are so simple in terms of the changes to code, that it wouldn’t actually save any effort to automate them. I don’t see a lot of room for improving the change from if to while, for instance.
Other transformations simply have an unspecified effect. For example, how would you automate the statement->statements transformation?
The crux is that refactorings keep the external behavior the same, and the tools depend on that to properly implement the refactorings. However, transformations don’t share that property.
Standardized Work
In the Specify/Transform/Refactor view of TDD, we write our programs by alternating between adding tests, applying transformations, and applying refactorings.
In other words, if we look at the evolution of our non-test code through a series of diffs, then each diff shows either a transformation or a refactoring.
This time we’ll take a detailed look at the transformations applied in the Green phase.
The Transformation Priority Premise
Most of you will have heard of the refactorings we apply in the last TDD phase, but there are corresponding standardized code changes in the Green phase as well. Uncle Bob Martin named them transformations.
The Transformation Priority Premise (TPP) claims that these transformations have an inherent order, and that picking transformation that are higher on the list leads to better algorithms.
Anecdotal evidence is provided by the example of sorting, where violating the order leads to bubble sort, while the correct order leads to quicksort.
After some modifications based on posts by other people, Uncle Bob arrived at the following ordered list of transformations:
Transformation
Description
{}–>nil
no code at all->code that employs nil
nil->constant
constant->constant+
a simple constant to a more complex constant
constant->scalar
replacing a constant with a variable or an argument
statement->statements
adding more unconditional statements
unconditional->if
splitting the execution path
scalar->array
array->container
??? this one is never used nor explained
statement->tail-recursion
if->while
statement->recursion
expression->function
replacing an expression with a function or algorithm
variable->assignment
replacing the value of a variable
case
adding a case (or else) to an existing switch or if
Applying the TPP to the Roman Numerals Kata
Reading about something gives only shallow knowledge, so let’s try out the TPP on a small, familiar problem: the Roman Numerals kata.
For those of you who are unfamiliar with it: the objective is to translate numbers into Roman. See the table at the left for an overview of the Roman symbols and their values.
As always in TDD, we start off with the simplest case:
public class RomanNumeralsTest {
@Test
public void arabicToRoman() {
Assert.assertEquals("i", "i", RomanNumerals.arabicToRoman(1));
}
}
We get this to compile with:
public class RomanNumerals {
public static String arabicToRoman(int arabic) {
return null;
}
}
Note that we’ve already applied the first transformation on the list: {}->nil. We apply the second transformation, nil->constant, to get to green:
public class RomanNumerals {
public static String arabicToRoman(int arabic) {
return "i";
}
}
Now we can add our second test:
public class RomanNumeralsTest {
@Test
public void arabicToRoman() {
assertRoman("i", 1);
assertRoman("ii", 2);
}
private void assertRoman(String roman, int arabic) {
Assert.assertEquals(roman, roman,
RomanNumerals.arabicToRoman(arabic));
}
}
The only way to make this test pass, is to introduce some conditional (unconditional->if):
public static String arabicToRoman(int arabic) {
if (arabic == 2) {
return "ii";
}
return "i";
}
However, this leads to duplication between the number 2 and the number of is returned. So let’s try a different sequence of transformations. Warning: I’m going into baby steps mode now.
First, do constant->scalar:
public static String arabicToRoman(int arabic) {
String result = "i";
return result;
}
Next, statement->statements:
public static String arabicToRoman(int arabic) {
StringBuilder result = new StringBuilder();
result.append("i");
return result.toString();
}
Now we can introduce the if without duplication:
public static String arabicToRoman(int arabic) {
StringBuilder result = new StringBuilder();
if (arabic >= 1) {
result.append("i");
}
return result.toString();
}
And then another statement->statements:
public static String arabicToRoman(int arabic) {
StringBuilder result = new StringBuilder();
if (arabic >= 1) {
result.append("i");
arabic -= 1;
}
return result.toString();
}
And finally we do if->while:
public static String arabicToRoman(int arabic) {
StringBuilder result = new StringBuilder();
while (arabic >= 1) {
result.append("i");
arabic -= 1;
}
return result.toString();
}
Our test now passes. And so does the test for 3, by the way.
With our refactoring hat on, we spot some more subtle duplication: between the number 1 and the string i. They both express the same concept (the number 1), but are different versions of it: one Arabic and one Roman.
We should introduce a class to capture this concept:
public class RomanNumerals {
public static String arabicToRoman(int arabic) {
StringBuilder result = new StringBuilder();
RomanNumeral numeral = new RomanNumeral("i", 1);
while (arabic >= numeral.getValue()) {
result.append(numeral.getSymbol());
arabic -= numeral.getValue();
}
return result.toString();
}
}
public class RomanNumeral {
private final String symbol;
private final int value;
public RomanNumeral(String symbol, int value) {
this.symbol = symbol;
this.value = value;
}
public int getValue() {
return value;
}
public String getSymbol() {
return symbol;
}
}
Now it turns out that we have a case of feature envy. We can make that more obvious by extracting out a method:
public static String arabicToRoman(int arabic) {
StringBuilder result = new StringBuilder();
RomanNumeral numeral = new RomanNumeral("i", 1);
arabic = append(arabic, result, numeral);
return result.toString();
}
private static int append(int arabic, StringBuilder builder,
RomanNumeral numeral) {
while (arabic >= numeral.getValue()) {
builder.append(numeral.getSymbol());
arabic -= numeral.getValue();
}
return arabic;
}
Now we can move the append() method to RomanNumeral:
public class RomanNumerals {
public static String arabicToRoman(int arabic) {
StringBuilder result = new StringBuilder();
RomanNumeral numeral = new RomanNumeral("i", 1);
arabic = numeral.append(arabic, result);
return result.toString();
}
}
public class RomanNumeral {
private final String symbol;
private final int value;
public RomanNumeral(String symbol, int value) {
this.symbol = symbol;
this.value = value;
}
public int getValue() {
return value;
}
public String getSymbol() {
return symbol;
}
public int append(int arabic, StringBuilder builder) {
while (arabic >= getValue()) {
builder.append(getSymbol());
arabic -= getValue();
}
return arabic;
}
}
We can further clean up by inlining the getters that are now only used in the RomanNumeral class:
public class RomanNumeral {
private final String symbol;
private final int value;
public RomanNumeral(String symbol, int value) {
this.symbol = symbol;
this.value = value;
}
public int append(int arabic, StringBuilder builder) {
while (arabic >= value) {
builder.append(symbol);
arabic -= value;
}
return arabic;
}
}
There is one other problem with this code: we pass in arabic and builder as two separate parameters, but they are not independent. The former represents the part of the arabic number not yet processed, while the latter represents the part that is processed. So we should introduce another class to capture the shared concept:
public class RomanNumerals {
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral numeral = new RomanNumeral("i", 1);
numeral.append(conversion);
return conversion.getResult();
}
}
public class RomanNumeral {
private final String symbol;
private final int value;
public RomanNumeral(String symbol, int value) {
this.symbol = symbol;
this.value = value;
}
public void append(ArabicToRomanConversion conversion) {
while (conversion.getRemainder() >= value) {
conversion.append(symbol, value);
}
}
}
public class ArabicToRomanConversion {
private int remainder;
private final StringBuilder result;
public ArabicToRomanConversion(int arabic) {
this.remainder = arabic;
this.result = new StringBuilder();
}
public String getResult() {
return result.toString();
}
public int getRemainder() {
return remainder;
}
public void append(String symbol, int value) {
result.append(symbol);
remainder -= value;
}
}
Unfortunately, we now have a slight case feature envy in RomanNumeral. We use conversion twice and our own members three times, so it’s not too bad, but let’s think about this for a moment.
Does it make sense to let the roman numeral know about an conversion process from Arabic to Roman? I think not, so let’s move the code to the proper place:
public class RomanNumerals {
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral numeral = new RomanNumeral("i", 1);
conversion.process(numeral);
return conversion.getResult();
}
}
public class RomanNumeral {
private final String symbol;
private final int value;
public RomanNumeral(String symbol, int value) {
this.symbol = symbol;
this.value = value;
}
public String getSymbol() {
return symbol;
}
public int getValue() {
return value;
}
}
public class ArabicToRomanConversion {
private int remainder;
private final StringBuilder result;
public ArabicToRomanConversion(int arabic) {
this.remainder = arabic;
this.result = new StringBuilder();
}
public String getResult() {
return result.toString();
}
public void process(RomanNumeral numeral) {
while (remainder >= numeral.getValue()) {
append(numeral.getSymbol(), numeral.getValue());
}
}
private void append(String symbol, int value) {
result.append(symbol);
remainder -= value;
}
}
We had to re-introduce the getters for RomanNumeral‘s fields to get this to compile. We could have avoided that rework by introducing the ArabicToRomanConversion class first. Hmm, maybe refactorings have an inherent order too!
OK, on to our next test: 4. We can make that pass with another series of transformations. First, scalar->array:
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral[] numerals = new RomanNumeral[] {
new RomanNumeral("i", 1)
};
conversion.process(numerals[0]);
return conversion.getResult();
}
Next, constant->scalar:
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral[] numerals = new RomanNumeral[] {
new RomanNumeral("i", 1)
};
int index = 0;
conversion.process(numerals[index]);
return conversion.getResult();
}
Now we need an if:
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral[] numerals = new RomanNumeral[] {
new RomanNumeral("i", 1)
};
int index = 0;
if (index < 1) {
conversion.process(numerals[index]);
}
return conversion.getResult();
}
And another constant->scalar:
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral[] numerals = new RomanNumeral[] {
new RomanNumeral("i", 1)
};
int index = 0;
if (index < numerals.length) {
conversion.process(numerals[index]);
}
return conversion.getResult();
}
You can probably see where this is going. Next is statement->statements:
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral[] numerals = new RomanNumeral[] {
new RomanNumeral("i", 1)
};
int index = 0;
if (index < numerals.length) {
conversion.process(numerals[index]);
index++;
}
return conversion.getResult();
}
Then if->while:
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral[] numerals = new RomanNumeral[] {
new RomanNumeral("i", 1)
};
for (RomanNumeral numeral : numerals) {
conversion.process(numeral);
}
return conversion.getResult();
}
And finally constant->constant+:
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
RomanNumeral[] numerals = new RomanNumeral[] {
new RomanNumeral("iv", 4),
new RomanNumeral("i", 1)
};
for (RomanNumeral numeral : numerals) {
conversion.process(numeral);
}
return conversion.getResult();
}
Now we have our algorithm complete and all we need to do is add to the numerals array. BTW, this should be a constant:
public class RomanNumerals {
private static final RomanNumeral[] ROMAN_NUMERALS
= new RomanNumeral[] {
new RomanNumeral("iv", 4),
new RomanNumeral("i", 1)
};
public static String arabicToRoman(int arabic) {
ArabicToRomanConversion conversion
= new ArabicToRomanConversion(arabic);
for (RomanNumeral romanNumeral : ROMAN_NUMERALS) {
conversion.process(romanNumeral);
}
return conversion.getResult();
}
}
Also, it looks like we have another case of feature envy here that we could resolve as follows:
public class RomanNumerals {
public static String arabicToRoman(int arabic) {
return new ArabicToRomanConversion(arabic).getResult();
}
}
public class ArabicToRomanConversion {
private static final RomanNumeral[] ROMAN_NUMERALS
= new RomanNumeral[] {
new RomanNumeral("iv", 4),
new RomanNumeral("i", 1)
};
private int remainder;
private final StringBuilder result;
public ArabicToRomanConversion(int arabic) {
this.remainder = arabic;
this.result = new StringBuilder();
}
public String getResult() {
for (RomanNumeral romanNumeral : ROMAN_NUMERALS) {
process(romanNumeral);
}
return result.toString();
}
private void process(RomanNumeral numeral) {
while (remainder >= numeral.getValue()) {
append(numeral.getSymbol(), numeral.getValue());
}
}
private void append(String symbol, int value) {
result.append(symbol);
remainder -= value;
}
}
Retrospective
The first thing I noticed, is that following the TPP led me to discover the basic algorithm a lot quicker than in some of my earlier attempts at this kata.
The next interesting thing is that there seems to be an interplay between transformations and refactorings.
You can either perform a transformation and then clean up with refactorings, or prevent the need to refactor by using only transformations that don’t introduce duplication. Doing the latter is more efficient and also seems to speed up discovery of the required algorithm.
Certainly food for thought. It seems like some more experimentation is in order.
Update: Here is a screencast of a slightly better version of the kata:
There seems to be some confusion between Test-First Programming and Test-Driven Development (TDD).
This post explains that merely writing the tests before the code doesn’t necessarily make it TDD.
Similarities Between Test-First Programming and Test-Driven Development
It’s not hard to see why people would confuse the two, since they have many things in common.
My classification of tests distinguishes six dimensions: who, what, when, where, why, and how.
Test-First programming and Test-Driven Development score the same in five of those six dimensions: they are both automated (how) functional (what) programmer (who) tests at the unit level (where) written before the code (when).
The only difference is in why they are written.
Differences Between Test-First Programming and Test-Driven Development
Test-First Programming mandates that tests be written before the code, so that the code will always be testable. This is more efficient than having to change already written code to make it testable.
Test-First Programming doesn’t say anything about other activities in the development cycle, like requirements analysis and design.
This is a big difference with Test-Driven Development (TDD), since in TDD, the tests drive the design. Let’s take a detailed look at the TDD process of Red/Green/Refactor, to find out exactly how that differs from Test-First Programming.
Red
In the first TDD phase we write a test. Since there is no code yet to make the test pass, this test will fail.
Unit testing frameworks like JUnit will show the result in red to indicate failure.
In both Test-First Programming and Test-Driven Development, we use this phase to record a requirement as a test.
TDD, however, goes a step further: we also explicitly design the client API. Test-First Programming is silent on how and when we should do that.
Green
In the next phase, we write code to make the test pass. Unit testing frameworks show passing tests in green.
In Test-Driven Development, we always write the simplest possible code that makes the test pass. This allows us to keep our options open and evolve the design.
We may evolve our code using simple transformations to increase the complexity of the code enough to satisfy the requirements that are expressed in the tests.
Test-First Programming is silent on what sort of code you write in this phase and how you do it, as long as the test will pass.
Refactor
In the final TDD phase, the code is refactored to improve the design of the implementation.
This phase is completely absent in Test-First Programming.
Summary of Differences
So we’ve uncovered two differences that distinguish Test-First Programming from Test-Driven Development:
Test-Driven Development uses the Red phase to design the client API. Test-First Programming is silent on when and how you arrive at a good client API.
Test-Driven Development splits the coding phase into two compared to Test-First Programming. In the first sub-phase (Green), the focus is on meeting the requirements. In the second sub-phase (Refactor), the focus is on creating a good design.
I think there is a lot of value in the second point. Many developers focus too much on getting the requirements implemented and forget to clean up their code. The result is an accumulation of technical debt that will slow development down over time.
TDD also splits the design activity into two. First we design the external face of the code, i.e. the API. Then we design the internal organization of the code.
This is a useful distinction as well, because the heuristics you would use to tell a good API from a bad one are different from those for good internal design.
Try Before You Buy
All in all I think Test-Driven Development provides sufficient value over Test-First Programming to give it a try.
All new things are hard, however, so be sure to practice TDD before you start applying it in the wild.
You must be logged in to post a comment.