Duplication-Driven Development?

What is the primary force that drives you when doing test-driven development? Surprisingly, it might just be code duplication.

In the book “Test-Driven Development by Example“, Kent Beck uses an implementation of money as an example of TDD. At one point, a test case says “assert(someFunc(5) == 5 * 2)” and to satisfy the test case, someFunc is implemented simply by returning a hard-coded 10. “Now that is dirty” you say (and that’s what I said :). According to the book, the reason for hard-coding is that we want the test case to pass as soon as possible. After that, the refactoring phase can take over and we can clean up the code.

The Petronas Twin TowersHere comes the interesting part: hard-coding is a terrible habit, right? And we don’t want our bad habits to show up in the code. This sign of bad taste should be reason enough to fix it, right? However, there is a more fundamental reason for getting rid of the constant: code duplication. The constant 2 * 5 = 10 occurs both in the test case and in the code. To remove the duplication, change someFunc(param) to return “param * 2” (later, the book also gets rid of the 2 by replacing it with a member variable). To sum up, instead of acting on “taste”, we use the well proven design principle of (avoiding) code duplication.

A fascinating insight from the book is that the design of the code, too, can grow naturally from code duplication. For example, let’s say we have a class that encapsulates some algorithm. When another class with similar behavior is required, the book suggests we can accept some code duplication (even extensive copy-paste) to pass the tests. After getting the tests to pass, we refactor. Lets say the difference between the two classes is the fact that they use different algorithms to carry out their work. Apart from that, they are identical. To avoid code duplication, we combine the two classes. Quite naturally, this results in us isolating the algorithms behind an interface. By following the path of least resistance for removing code duplication, we have implemented the strategy pattern. To summarize, this suggests that code duplication helps us drive the high-level design of our code. If true, it is kind of a comforting feeling that refactoring and code duplication naturally leads us to good design.

All and all, “Test-Driven Development by Example” is well-written, easy to read and quite a good book, albeit maybe more suitable for the aspiring test-driven designer than the seasoned on.

Eclipse Won’t Run All My JUnit Tests

Here’s a Eclipse/JUnit4/Maven problem I came across, maybe this “solution” can help someone else.

I was using Eclipse to unit test some Maven projects in Java. I noticed that Eclipse only executed two out of my four JUnit4 unit tests. Right-clicking a test case and choosing “Run” on it gave me an error: “Unrooted test”. The more popular tips from Google (e.g. Nabble and Stack Overflow) suggested the code was ok, but still Eclipse was skipping my tests. Then I found this post on RTFM which said “recompile your code using mvn clean install“. Rediculously simple, but it worked for me. (Ironically, running Maven is something I normally do twenty times per day…)

The funny thing is that JUnitMax (which automatically runs all tests when a file is saved) managed to run the skipped tests even though Eclipse couldn’t! I could tell it correctly passed/failed the test when I changed it. I am still confused about that. :)

Lean Software Development

I just finished reading the book “Implementing Lean Software Development – From Concept to Cash” by Mary and Tom Poppendieck. In essence, “lean” means “reduce waste”, and “waste” means “everything that does not make your customers happier”. Many concepts in the book was pioneered by Toyota while applied to manufacturing. Toyota then moved on and introduced lean in product development (including software).

Implementing Lean Software DevelopmentWhen a customer ask you for a feature, how long does it take until they get it (calendar time)? While implementing it, how much effective time have you spent on the feature? The difference between calendar time and implementation time is waste (since waiting makes your customer unhappy). This waste can be analyzed using Value Stream Mapping (example). Typical things that takes time for no good reason is waiting for approval (solution: approve within 24h instead of in the bi-weekly meeting) and long fixed releases. I have come across really long release cycles in previous work places and when talking to friends at other companies. (Half a year, a year is not unusual, which should be contrasted with the book’s claim that Toyota developed and released the Prius within a year and a half!). The solution is to release and/or deploy more often. In order to do this, we need a relentless focus on quality (= no bugs).

According to the book, having bugs in a bug tracking system is waste. Software with bugs in it is not finished work, and piling up unfinished work is waste (since it cannot be released to your customer until it’s finished). Also, someone has taken the time to enter the information, and someone must look at it to resolve the bug. Meanwhile, someone else might stumble across the same bug. All this takes time. Lean software development says you should solve the bugs right now. In order to make changes to solve the bug right away, you need tests to make sure you don’t break something else. Good quality software starts with rigorous and automated tests on acceptance and unit level (see my previous posts: test-driven development done right and edge-to-edge unit tests). Reducing the extra work from bugs will free up resources to implement new features.

Lean software development is about continuous improvements. Every day, we should ask questions like: How do I make my customer happier? How do I reduce waste in order to release faster? But who should answer these questions and implement the improvements? Lean says it’s the people closest to the problems. Inevitably, in software development, it will be the programmers! I think this is the most appealing part of the contents of the book.

This is a fantastic book and I am sure I will read it again pretty soon. Read it!

Edge-To-Edge Unit Tests

The term unit test implies at least two different things. First, it means testing your code at the smallest unit, which is a function or a class (well, technically, you test the methods of a class, but they would make little sense in isolation). Second, it means writing test code in a language to test functionality in the same language.

Normally, when I write C++ code to test my C++ functionality, I tend to stay away from the “unit” level. Instead, I like tests that exercise the system edge-to-edge, resembling the interactions with the outside world as much as possible. Now, full edge-to-edge testing is normally not possible since the peripheral parts of a system are often hard to control. For example, let’s say I have a system with network on one side and a GUI on the other side. A realistic test case would have traffic over the network and a GUI reflecting that. But taking the network as an example, it complicates testing due to issues like slow response times, need for a remote side and network failures. So I settle for testing up to the interfaces of the network and GUI: you would inject network “traffic”(or time-outs) on the network interface, verify that the GUI interface is told to show something, do some user input on the GUI interface and watch outgoing network traffic being generated.

As with everything, there are pros and cons when testing at this level. To me, the main benefits compared to low-level unit tests are:

  • Having tests at that level gives me confidence that there’s a reasonably low probability of faulty interaction with the outside world.
  • The boundaries of your system are much less likely to change than the internals. This means you are less likely to spend time changing your tests.
  • It is easy to argue for the business value of the tests. They correspond well to what the customer expects and are used to guarantee the quality.
  • The tests are a decent measure of progress. Having a passing test means you are close to something to show to your customer.
  • It’s fun! And you can test-run your system before you have a network and a GUI.

The downsides I’ve experienced compared to testing at the unit level are:

  • Tests like these can make it harder to achieve decent code coverage. For example, your code might involve randomness, timing issues or use of the current time. You will have to make sure these can be controlled from the test context.
  • High-level testing can be hard to introduce late in the development process. For this to succeed, the whole system must be designed for testability. See the previous item.
  • The tests become monolithic. I’ve come across the situation where parts of my system were broken out to form a new shared component. The new component has to have tests of its own (or someone changing it won’t notice it’s broken until they run your tests). Your tests use the classes of your system, which are not suitable to use in a shared component since dependencies would go the wrong way.
  • It might be overkill for testing some parts of the system. For example, if you have some deep-down string manipulation code, you should go ahead and unit test it (in the true sense of the word). It’s all about choosing the proper tools for the problem.
  • Due to complexity in the lower levels of your software, you might be facing a combinatorial explosion of different test cases. You will have to select a few representative test cases and resort to normal unit testing of test the low-level parts. See the previous item.
  • Testing on this level poses sort of a communication problem. If I call my tests “unit tests”, most people think only of tests on the lowest level. If I call them “acceptance tests” or “functional tests”, someone will inevitably assume I have properly tested the system from the outside, edge-to-edge (which is definitively necessary, even with the tests described above). Calling them e.g. “functional unit tests” only adds to the confusion. (“What do you mean? Is it a unit test? Is it a functional test? Surely, it can’t be both.”) If you know of terminology that could help, let me know. Until I hear from you, I will just call them Edge-to-Edge Unit Tests.

As I said before, it’s about choosing the proper tool for your problem. If at all possible, I resort to “unit testing” at the highest possible level. If you haven’t done so, you should give it a try.