Sunday, 14 October 2007

FreshReports 0.6 is out

PUff, finally I got this paging thing working.. more or less. I have put so much effort into it, I decided to make it a new release.

In fact, I made paging work from the third attempt. First time I took the path of Need-Driven Development, as I understood it. Namely, instead of writing a functional test, I worked step by step and mocked the stuff I planned to add later. The result was a disaster -- I quickly lost all understanding of what works and what is left to be implemented yet. So, at some point I decided to hit the "delete" button.

Second time I did it better. I already had a Page class that held all the element positions in a hashtable. I was pretty sure that once an element appears in this class at a correct position, it should be printed correctly. However, I was treating "unbreakable" sections (ones that cannot be split across pages) as single elements (thus having a single entry in this hashtable). Once I had all the possible tests i could think of pass, I was pretty sure that I got it.

Well, I launched my test application and promptly discovered that everything is printed at wrong positions even if it fits on a single page!

Had to start again.

This time I decided to keep that hashtable, but now I keep all the individual elements' positions there, not sections.

This change broke about half of my tests. I suspected that my tests are a mess, but not that much. Turned out that most of them are not "unit" enough, nor integration. I remember that I should refactor my code, but I'm forgetting about refactoring my tests.

Anyway, I think I've nailed it this time. The code is very ugly at the moment, so I'm not releasing it. I'll be refactoring it as well as add some additional checks about stuff like page margins and such. Probably the next release will see the section margins as well, to get finished with the layout features. But the most important things now are grouping support and aggregates.

Monday, 1 October 2007

TDDing a new feature into the existing code

Suppose you have managed to find out the conditions that lead to a crash. How do you fix it in a TDD fashion? Or, how do you add a new feature to the existing code?

I usually try to resist using the existing code at first. Instead, I modify my production code so that the new conditions take a new path of execution. For example, I assign a certain ID to some object that is part of my test setup, then in the production code I check for this ID, and if it fits, I take the new path. This way I make sure that all my previous tests go to the old path, and since I don't touch the old path code, they will pass for sure.

Next, I force the new test pass as usual. I don't even need to check the other tests -- they are not affected.

Now comes the interesting part. I'm refactoring the new path as usual, watching the new test, but I'm trying it the way it looks "similar" to the old path. I might factor out a new method that looks like the one that's been used by the old path. Sometimes I could even use an old method. If the new test breaks, the old method needs to be modified. Or, I could modify the old method (now I should also watch the old tests). If it's too complicated to incorporate the new behavior, I repeat the same trick: split the path into two, and try to make it one in smaller steps.

With each small step, the difference between the two paths diminishes, so at the end we have a common path of execution, and we can remove our initial split.