Tuesday, May 04, 2010

Small steps

Today I was working with a Java framework which runs on top of OSGi (another framework, and yes, Java is verbose even from the platform stack point of view.)
After having prepared a (hopefully) failing acceptance test which used httpunit to extract the content of a dynamic page and verify that the world "Hello" is contained in it, I was ready to run the test, see the assertion failing and start writing our beloved production code.
Unfortunately, the test failed not because of the assertion, but because the JUnit Plugin runner, which takes a JUnit test and run it over a configured OSGi framework, was saying "No tests found." How that was possible?

When tests that should drive the development leave you without a clue on what is happening, chances are that you are taking big steps when you should walk carefully. I was taking big steps because it takes two minutes to run such an acceptance test due to all the infrastructure noise, but I'm not an expert in OSGi applications and I should have taken smaller steps towards my goal of a failing test. The same is true for production code: if I can't get a green bar maybe my failing tests are not fine-grained enough.

Thus I run a git reset and perform the following baby steps to build confidence:
  • create an empty bundle (jargon for package), and write an empty test in it. Run and see Warning: no assertions, but nothing explodes.
  • Add a test method with a assertTrue(true) call. Run, and nothing explodes still.
  • Add httpunit in lib/ and set it up with the Bundle-ClassPath directive, which basically says where the runtime can find the library JARs in the bundle. 

Ok, it was that. After a quick search on Google, I found out that I was specifying the classpath as I've always done via command line or ant scripts:
Bundle-ClassPath: .:lib/httpunit.jar
while OSGi has a different opinion of classpaths:
Bundle-ClassPath: ., lib/httpunit.jar
So I lost even the current directory (.) from the classpath when introducing the first, malformed directive.

After having located the problem with a test-first approach, I was able to (slowly) run my red test and return writing production code.
Automated testing and incremental development save the day again - imagine what would have happened if I had to debug this mess manually, with a framework that takes some minutes to start, and no idea of where the error was...


fqqdk said...

yay :) is this story connected to when you were talking about selling cigarettes to children just to get a green bar? ;)

Giorgio said...

Oh that was the day before, when I hacked in the same framework's code to get my test green... the patch will stay there until I find a different solution, with proper source control it's simple to track my hardcodings. :)

Crias said...

I like the article. Two minutes to run a test though? Ouch!

There may be some ways to significantly bring that time down. I've done TDD in OSGi-land, drop me a line and maybe I can see if I can help.

Giorgio said...

It is not really a *unit* test if it must run in an OSGi environment, so I can accept it as a functional test that has to build a large object graph before doing real work. The time goes for booting up all the necessary bundles (at least on my non-high end single core machine), so the framework's developer suggest to use an AllTests class for every test bundle that does the initialization only one time and run all the tests in the same environment. I'm also setting up a custom configuration for every test bundle that defined only the mandatory bundles.
This does not help with running a single test, but I'll do development with real unit tests that run out of OSGi as much as possible.