Thursday, July 24, 2008

My experience with unit testing

I am not new to programming but have had bad programming habits for too long. "Enough is enough", I said to myself one day and decided to use a test-first approach for the next project.

Here's what I experienced.

1. Although I created the test cases before writing the classes, I didn't keep up with the policy for long. I found it a little distracting to think of and write a test case before any change to my code. The result was that soon my test cases were totally out of sync with the code.

2. It is difficult to be disciplined enough to keep test cases up to date specially when you are working with tight deadlines. Effort estimation must take into account this fact.

3. I had to continuously refactor my code to make it more testable. Writing test cases turned out to be a great way to write well formed APIs and good code in general. I had to make several modifications to my initial design to allow dependency injection. Moral of the story - automated unit tests can't be implemented as an after thought.

4. I found it difficult to test private methods and had to change their visibility to test them.

5. I found it difficult to automate everything. There were times when I just wanted to print the values of a hashmap and inspect manually. I found it too time consuming to completely automate the testing in such cases. Perhaps it's a case of old habits. But I realized how much of our testing is based on manually inspecting the results. It is difficult to break this habit and to learn to rely on automated test results.

6. Mocks are confusing (jmock) but very very helpful.

7. Seeing the unit tests run successfully didn't give me much confidence except for the first time when I ran them with fresh code. I can think of several reasons for it. Firstly, as I mentioned before, manually verifying the results of a testcase gives one a confidence that's difficult to achieve by running automated tests. There is something reassuring about seeing the results with your own eyes. Secondly, my test cases were not comprehensive enough to test full functionality. So after some time, I didn't see much value being added by running these tests. And lastly, I was coding for a database intensive CRUD application; my code was heavily dependent on file i/o and database access, both of which needed extensive integration testing. After a while, the unit tests were rendered completely useless and I was hardly running them.

8. There is only so much that unit tests can achieve. Most of the bugs in my code were caught during integration testing. But I must admit that at least some of these could have been caught during unit testing if I had better unit tests.

10. There should be a set of integration tests that check essential functionality. These tests may not run during the continuous build but must run during the nightly build.

Thursday, July 10, 2008

Java Code Review Tools

Today I gave a presentation to the team on java code review tools. I covered 2 tools in detail, PMD and Jupiter. while I was talking about them, I realized that both the tools can do with some improvements.

PMD
  • Improved filtering to allow me to run the tool on selective files.

Jupiter

  • Must let me view defects data in a more user friendly way. I want to be able to view all defects and then aply filters on them. The functionality is there but not in the way I want it.
  • Must allow me to generate reports on defects metrics.
  • The workflow is confusing (why should i choose a reviewer in the 'rework' phase ?). The process should be simplified.
  • There should be a filtering icon in each view.

Note to self: Explore option of adding these features yourself.

Wednesday, July 9, 2008

Use groovy as your scratchpad during java development

How many times have you wanted to test out a small piece of java logic before implementing it in your code?

In the past, whenever I did something 'smart' with Dates or Calendar or Strings or StringBuffers, I wrote a small java program (often called Test.java) to test the logic I had in mind. The process, as we all know, is slow to do in java because of Java's write-compile-run cycle. Even if you are using the most sophisticated IDEs, it is still a chore.

Enter Groovy

Groovy is a dynamic language written for the Java virtual machine. You can use Java syntax in a perl like language. While Groovy is a full featured new-age programming language (I am having lots of fun learning it by the way), I use it more often to test out bits of java logic before I want to introduce it into my code.

The other day, I wanted to check how GrgorianCalendar's firstDayOfWeek is affected by the Locale. Normally, I would have written this in Test.java but not any more. I opened the Groovy console and typed in the code snippet I wanted to test. I didn't have to create any class or import anything (java.unit.* is imported by default). There was no manual compilation needed (because it happens under the hood). All I had to do was type my code and press Ctrl-R. Presto. The output was displayed in the output screen. I changed the Locale in the constructor and again pressed Ctrl-R to re-run. No recompilation, nothing.






I recommend this tool to every serious java developer as a productivity tool. Of course once you get hooked to Groovy as a programming language, I am sure you'll find it as interesting as I do.



Working on maintenance projects

first posted on personal blog on Monday, November 13, 2006

It is difficult to understand why people have such an anathema towards maintenance projects. I often come across programmers whose enthusiasm takes a nosedive every time they are asked to work in a maintenance project.

I think, it is an attitude problem more than anything else.


Most projects that I have worked on have been either maintenance (bug fixing) or enhancement projects. If one has the right kind of attitude, working on a bug fixing project can be a very fulfilling experience.


What attitude, you ask? I suggest the following.

All bugs can be replicated and fixed.

All bugs can be detected, analyzed and fixed. If you can't replicate it, don't give up. Check that you are following the right steps. Make sure that your assumptions are correct. Investigate and find out more about the conditions in which the bug was reported.

The software doesn't control you. You control it.

A program does what it is told to do. So even the seemingly erratic/intermittent behavior will have some logical explanation. Don't be afraid of ripping open the class files using a decompiler if needed. As a bug fixer, you are like a private detective. You have access to all areas of the application and you must go through all the code if needed. I once saw a colleague going through the decompiled code of log4j. He was apparently getting some trouble with logging and wanted to understand log4j code to see what was wrong.

...And it's all small stuff.

Don't be smug. Don't ignore trivial causes. Are you running the right build? Are you connected to the correct database? Are you using the right version of code? I can't begin to count the number of occasions where I have done this mistake or have seen someone else doing it. It's like forgetting the attachments in your emails. No matter how experienced you are, you will still catch yourself doing silly mistakes every once in a while.


Be humble.

As I said before, it's software you are working on. Software will have bugs. Even after you fix them, there will still be some more. So don't take it personally if someone points out a bug in your code or if the fix that you provide doesn't work. Be humble and accept your mistakes. I hate to see programmers getting defensive about their code or design.


Pretend that the "Go to" guys don't exist.

Do not get into the habit of raising an SOS every time you see a NullPointerException. There will always be a few star programmers in your project but it irritates them beyond belief if you go asking for their help for every stupid exception that your program throws. Try to fix the problem yourself first. That's what separates good programmers from lazy nincompoops.


Do your homework before seeking help.

In other words RTFM and save the logs. If you do need to take help, do your homework first. Read the specs, do some research, google the error message and find out if someone has already faced it before. Post your query on the forums. And for God's sake, save the logs before calling someone for help.

Be lazy. Automate!!

I learnt this very early. Best programmers are creatively lazy. Automate. Find out tools that can help reduce your time. In short, be a smart coder. If you are working on legacy code, you will need powerful tools and good skills to search the codebase. Learn regular expressions and use them in your searches.


Think out of the box.

People have been known to recover from the dreadful "rm-rf" that someone accidentally typed on a Unix box. The point is, no problem is big enough that it can't be solved. There must exist a solution. And you should find it. Think laterally if needed.


Always get the big picture. Don't forget to ask the "whys".

This is very important. There is a reason why everything works the way it does. There is a reason why your client chose Java over C++ (or vice versa) and there is a reason again in why they chose to not use entity beans. Be inquisitive. Question everything. Get a bigger perspective.It will help you come up with better solutions to problems that you are working on.