Thursday, October 15, 2009

Venkat wrote a nice article on when to use mock. He made a point that by gaining deeper realization on domain problem, we can make our design simpler. As a result, we will not need a mock object. I feel that the role of Test-Driven Development (TDD) as a design tool deserves more attention. This is the most misunderstood part of TDD. Many people think that TDD is about writing test. But, it s more than just testing. We are also designing our application at the same time. I prefer the term Test-Driven Design over Test-Driven Development.

Test After

We will look at a simple example to illustrate the difference between test first and test after approaches. Let's say we we are building a billing system for a clinic. One of the functionalities is to collect payment from a patient for the services tendered. We will use Test After approach. As an exercise, I created a Patient.java and a getPayment() method. It came to me naturally that in order to calculate amount owed by a patient, I would need a list of services that are provided to the patient. So, I added a parameter to my getPayment method. The implementation looks like the following:

We can easily implement similar test to test for non-zero service list. The above test is good enough for our discussion. So far, everything looks good. I am happy with this implementation.

Test-Driven Design (Test First)

In Test After, I implement getPayment method by thinking of what do I need in order to implement the method. In test first, I have to change my mind set. I need to think of how to create some test code to test the getPayment method. In other words, I need to think of how to use my code in the test. Below is the test code I produced by using test first approach:

public void testGetPayment() { Patient patient = new Patient();

patient.addService(new Service(LAB_WORK));

assertEquals(patient.getPayment(), 15d);

}

The above test method is different from the previous one. The difference is I don't pass in a list anymore. When I write test first, I am thinking from a patient perspective. When a service is tendered, it should be added to the patient. It is just like an on-line shopping cart. A customer can add any items in store to his/her basket. Similarly, a patient should be able to add services tendered to him/her. Below is the new getPayment implementation.

Another benefit of writing test first is we can change our code easily since no actual code is written yet. If we do test after, we tend to avoid making changes to our implementation even if our design is wrong. We will try to tweak our test to comply with our implementation. As a result, our test may still pass even though our implementation does not work correctly.

Further readings:

Neal Ford wrote a nice two parts article on Test-Driven Design with more advance examples:

Sunday, October 11, 2009

Code maintenance is a big part of a developer's job. After we write our first method and then go back to fix it, it is a maintenance job 1. This is true for developing a new application from scratch or making enhancement to an existing application. But, developers often don't see it that way. When we inherit a poor written application, we lose a lot of freedom and creativity in creating our own solution.

I happen to inherit a poorly written application from time to time. When I inherit such application, I am not very motivated to work on the application. Everyday is a torture for me to just look at the legacy code. The code is poorly written, application architecture is accidental, no test cases or some of the test cases don't have assert statements. I complete the tasks I am asked to do by putting down the solution that first comes to my mind without refining it so that I can finish the project as soon as possible and move on to the next project. But, what if the next project is worse than the project I had before? So, the evil cycle continues.

I didn't resolve to hacking my solution overnight. At the beginning of the project, I was determined to make the project better by recommending some of the pragmatic approaches to be adopted in the project. I had a long discussion with the team and everyone thought those were good ideas. But, everyone had higher priority on their plates and none of the good ideas were realized. I tried to put in some tests, refactored some of the code that I was working on so that it is testable. But, no matter what I did, it didn't seem to have an impact to the overall application. So, I gave up and went down to the code hacking path. I fell into the trap of Broken Window Theory.

The truth is, I lost focus. I care too much about the end result. I should focus on completing today's tasks to the best I can by refactoring the legacy code into testable code and refactoring long method into smaller methods using composed method pattern. I should fix one broken window at a time and not attempting to fix all the broken windows overnight. If I have done something to make today's code better than yesterday, I have achieved something and I should be happy about it1. I may not see a big difference as a whole but incremental improvement is the first step to improving the whole application. If I stay on this path, the developers around me may see the benefit of the good practices I am adopting and start following.