Tests and TDD in anger
This week we started to make some changes to the FX code. The existing code made some strange assumptions about some of the edge cases and the resulting display was occasionally inconsistent. We fixed that, and the tests saved us from a couple of embarrassing mistakes.
Meanwhile, in my free time, the test driven POP3 server development continues. I now have a working server that can run off of a message store that lives on the file system and can do all the things that a POP3 server is supposed to do. So, did the test first approach help?
First the FX refactoring: The tests worked well to support the changes we were making this week, but the lack of sufficient granularity slowed things down. We deliberately decided to test at the inflection point where the FX code touches the rest of the application and the user, and we knew that this would provide insufficient coverage but decided that it was the best way to spend our time. The tests we have saved us from shipping some embarrassing mistakes this week, but it took a long time to get from the test shouting “You’ve broken it” to us working out exactly how we should do things so as not to break it…
The POP3 server has been developed completely test first. Well, OK, I admit I slipped a couple of times; some things seemed so simple that there was no point in writing a test first… They weren’t as simple as they looked and on both occasions I moved back to test first pretty quickly.
TDD seems to work well with my desire to make use of all available time no matter how small a time slice I have. I found that because of the tests I could do useful work in even smaller time slots than I’ve been able to before. 10 minutes whilst the potatoes boil? No problem, the last test shows me where I was and the next step is very small… The fact that all code is tested and testable means that you can dip back into the code at any point and feel comfortable. 5 minutes to spare before a meeting? You can do a little bit of refactoring safe in the knowledge that as long as the tests run you’re pretty safe. If you come back to some incomplete code that breaks tests and that you don’t understand you can just throw it away, there wont be much of it. So, TDD seems to be a tool that is compatible and supportive with how I like to work.
The testing seems to have driven the design nicely. The ability to integrate early and demonstrate a working POP3 server well before I’d even began to think about the message store was nice and could have proved useful for downstream clients. Some refactoring is needed but in general things work pretty well and the code is quite clean. The testing pulled the design in a ‘simplest first’ direction. This means that it’s not actually much like the design I originally thought of. There are a couple of places where I expect I will need to improve performance; at present I read an entire mail message into memory and then send it to the client. I’d personally rather read and stream a chunk at a time using overlapped IO, but I can write a test with a huge email and watch the performance suck and then refactor… I know I will need to do the work eventually but right now I have something that works 90% of the time and again it could be demonstrated or released. If I’d followed my nose I’d still be working on the overlapped read and stream method and I wouldn’t have a server that I could point Outlook at and have it retrieve mails.
Is it faster to develop this way? I don’t know. I should have kept track of the time spent on the project but it’s a personal thing and I didn’t bother. I will next time. I’ve been spending odd hours and minutes on it in the evenings. Total elapsed days 11, estimated actual working days maybe 1-2. Even if I had the timings it would be hard to know if this was quicker than not doing the testing… It felt faster and it felt like it flowed better and was more controlled. I could have reported back with useful and truthful answers to questions like “how complete is this bit”, etc.
There’s more test code than production code. Using the Project Line Counter from WndTabs.com I find that I have 1,461 lines of code in the POP3 library and 2,283 lines of code in the test harness. I don’t use CPPUnit yet as I haven’t found I need it, perhaps this leads to the tests being a little more bloated than they need to be? If so, I don’t think it’s much. The tests are doing a lot, especially now that we have a message store to worry about. The tests for the message store and mailbox classes use a helper class that creates an appropriate file system structure on disk, complete with mailbox directories and email contents… I’m not overly concerned about the production/test code split. I expect I can reduce the amount of test code by doing a tad more refactoring…
So, what’s next? A quick pass through the code and tests to refactor and then on to writing a POP3 client that can download messages and populate my message store.