Search This Blog


Tuesday, February 19, 2008

How to Evangelize Unit Testing

You can't force others to practice unit testing, especially if they are working on legacy applications. They will have a lot of excuses and true enough, doing unit testing on legacy code that have not been designed for testability is hard. But not doing any automated testing is tantamount to not paying your credit card bill. Sooner or later your debt will eat you up and you will go bankrupt. Or not, if you are lucky, you can pass your debt to either your descendants or successors and let them worry about the problem.

But sometimes you don't have the luxury (i.e., passing the mess around), worse still, you may need to anticipate the messy code problem because you may have to do the cleanup. Or you are such a purist that you can't see the whole application walking around without automated testing covering its ass, then my experience below maybe helpful to you.

First, a little background.

We did our application in C#. Since the language is fairly modern, so it must follow that the software development style should be fairly modern, right? Unfortunately, no.

We didn't have unit testing until fairly late into the development. And so, I was in charged of brining the unit testing practice to the company because I was a fierce proponent of it.

One thing I can tell is that it's never easy to convince the developers to adopt new things unless you can show tangible benefits. But there is a chicken and egg problem . Since the benefits of unit testing is only apparent when you work it on long term, how can you prove to them that unit testing is a good thing? Unless you can take over their projects and do the test cases then it's a different story. But on a large scale commercial application this is simply not possible. Unit testing is a bit like religion; you can't taste the goodness unless and until you become a convert.

One may want to show that the code with unit testing coverage has the lowest bug count. But this number is not easy to get. And even if you can do that, so what? Using bug count to evaluate developers or to promote unit testing is no better than using bug count to evaluate testers. If you have very few bugs in your code, it could have meant that your code is still not tested, or that your work is so easy that there isn't much bugs lurking around, or that you are already a superior coder for your program is much better than the guy seating next to you, regardless of whether you do the unit tests or not.

The only way I can think of is to create integration test cases.

Well, it's not exactly integration tests. It's a hybrid between the integration tests and the unit tests, leveraging on the existing NUnit framework.

Sounds complicated?

It's not, they are just NUnit test cases that test large modules with different components interacting together. Since those test cases were not testing individual methods, so they were not exactly unit testing. Low level methods and classes were just too tightly coupled to be tested individually. So a compromise had to be made. Luckily we still had some high level design and low coupling between different modules, so creating NUnit test cases on module level were still feasible.

Since the developers hadn't bought in the idea, so I created the test cases for them.

This must be done carefully. The developers usually didn't like other people creating test cases for them because they would feel like they were not trusted. A political revolt might result if one did this briskly. Besides that, the code was also not structured for maintainability so you might need to refactor the code first before you could write tests. Generally, the developers' willingness to let you touch their code is proportional to the code quality. So the worse the code is, the more need there is to create tests for them. But the worse the code is, the harder it is-- either technically or politically--to test them.

This is a vicious cycle.

What I did was I appealed to the boss to lobby for automated regression tests. Luckily he fully understood the problem and gave me a go-ahead sign. I also reasoned to the developers and asked for their permission to
  1. Refactor the code
  2. Create test cases to test their code
At that point of time, the developers would normally agree to let you did the work, regardless of how grudging they were at the beginning. After all, they needed not to do extra work and since the boss had agreed on it, so there was no reason to complain.

After receiving the green lights to create integration tests, I understood that I must did this correctly, on the first attempt. There would be no second chance if I couldn't show the tangible benefits within a short time frame. Everyone was impatient to see the results. If they couldn't see the results in two months time, all the good will and enthusiasm would drop off exponentially and to pickup unit testing next time would be significantly harder.

Which was why it was necessary to go for the biggest bang for buck.

The next step I did, I stategized and look for the area that was most error-proned and easiet to create tests on. You wouldn't normally get both, but in my case, I was lucky to find the weakest spot and started to create tests on it.

And being the person who didn't write the code, one might not know what to test; afterall, unit testing was supposed to be done by the developers who knew their code inside out.

So to work around the problem, I went through the bug tracker to look for relevant bugs. Since all cases had ( or were supposed to have) the reproduction steps, the observed outcome and the expected outcome, I knew how to reproduce the problem, what were the correct and incorrect results. And so, I knew what to test and how to test. Problem solved.

So, as test cases accumulated, the ability for the entire test suit to find bugs increased gradually. Seeing that the tests did catch bugs, the attitude towards unit testing changed gradually as well. After sometime I passed the whole test suit to the respective developers and asked them to maintain it themselves. Now they were willing to own the test suit.

I think there is an important point here. To help the developers to see the virtue of good software design, it is often necessary to let them realize the benefits of unit testing first before anything else. Too often the developers who write bad code will be reluctant to cleanup their design mess under various pretexts. But once their buy in unit testing, they will naturally design their code to be testable and will pickup good software design practices along the way.

No, we are still far from Test Driven Development (TDD), but at least the developers were more receptive to unit testing and started to create their own tests. Well, occasionally they might still forget to do it, but at that point, a little reminder could solve the problem. My personal experience with unit testing was that it was infectious; once I started doing it in one area of my code, I would like to apply it onto other areas. And with time I learn how to write test first, let the test drives the design!

I believe that gradually every developer would start to write test cases themselves automatically, without reminder once they got onto it.

Follow up post: How not to evangelize unit testing

No comments: