How I fell in love with test-driven development - Part #1

by Balazs Beregnyei
|
8 mins read
|
in 
  1. Development
Decorative image for How I fell in love with test-driven development - Part #1

Test-Driven Development (TDD) is not a new thing at all, it’s been around for decades. I was amazed at how our Ruby on Rails developers implemented it while working on a Screamingbox project not long after we founded the company. I loved the idea of writing the test first, seeing it fail, then implementing a new feature and making the code pass through. It quickly became a buzz in web development. On the other hand, my professional area, embedded development, was quite different. I saw mostly manual tests around me. Some unit tests here and there, some special hardware designed for testing and programming the final version of the devices under production at the factory, but that’s all. No TDD during the development in general.

I felt for a long time that it isn’t for me for two reasons :

One: my codes run on embedded hardware which we cannot really simulate. Sometimes we are tied to a special toolchain which is not fully compatible with other compilers of the same programming language.

Two: writing tests for all new pieces of code seems double effort, double time, double cost for the end client. I had no mission critical, medical, aeronautical projects in sight. Who would any of my average startup clients pay double for TDD?

Both assumptions turned out to be false later.

I have a hobby project, writing a simple chess program just for fun. It does not have to look good, because I am a programmer, even a text console would work perfectly. It does not need to be powerful, because I am not a professional player. Besides the joy of creating something, the main goal was to have a chess AI which is a good training partner for myself. Other available chess AI’s aren’t really friendly to me. They could make ridiculous moves on Level 1 like throwing away their rook, but on Level 2, I don’t even have the slightest chance to win. No matter how many times I reverse a bad move, it always turns out that there is no root cause for losing, I would lose anyways. Quite frustrating though, it had to be fixed.

In The Beginning

My first version was written in C about 10 years ago. Frankly, the quality was quite bad, although I had my best chess tournaments against a computer ever. I had a long pause after. Client projects came and went by. Then I had an upcoming transatlantic trip with really long layovers, and I was wondering what’s the best meaningful offline work I could do. Yes, the chess AI! During the trip I rewrote it all from scratch in C because I thought it could be an interesting feedback on the improvement of my skills as a programmer. Sure I could do much better! Well, it did not go as smoothly as I expected. The 2nd version was much faster, played a bit smarter, but sometimes it also did dumb moves. Traditional chess AI’s are mostly about brute force: generating all responses for all the possible next moves, and then their responses, and so on, typical exponential growth. We could significantly reduce the search tree by using some well known techniques (alpha-beta, ordering heuristics), but still, we need to generate a lot of moves in order to decide what's next. I was unable to fix my code, because I was lost creating tricky filters for finding a needle in a haystack, the one bad move in a search tree containing a million moves.

During that time I was totally into watching all the Clean Code videos of Uncle Bob (Robert Cecil Martin), our recent podcast guest . I have got new inspiration to give TDD a real chance. It was a nice coincidence that my problem solving struggle was a pretty good match with my actual Youtube content. The project wasn’t an embedded one, and it wasn’t billable, so none of my two previous assumptions played any part in my decision to rewrite the code again, and finally in TDD. This time I picked C++ instead of C. Normally I wouldn’t have picked C++ for this kind of a problem, because the implementation is mostly about manipulating and mass generating some well packed byte structures and bit fields with doing as little overhead like dynamic memory allocations as possible. Of course C++ could do that too, but my focus was on how to create good structures that can be easily testable. The only fancy feature I wanted to add to the 3rd version was a simple graphic interface based on Qt, a cross-platform framework.

The 3 Laws

Honestly I wasn’t able to follow Uncle Bob’s Three Laws of TDD every time precisely. Sometimes I wrote a bit more code than I was allowed by the tests I wrote before or, I wrote more than one test at a time. But in general, every functionality was built together with good tests. I achieved bigger progress in the first 3 days than I have ever imagined before! The AI just started generating meaningful moves. I was impressed. After one more week my hobby time was over, but I was at a milestone anyways. All the algorithms I wanted to implement at this stage were ready, and the AI played quite well.

It’s time to talk about the two surprising facts that I have learned here.

  1. Doing TDD does absolutely not cause double effort, it is actually the other way around. Even if it seems so at the beginning while we are working on the tests, it pays off really well when it comes to bug fixing. Well, we don’t even need a long post-programming period of stabilizing, testing, bug fixing. More than that, there is no such thing as stabilizing. Sure, this project was a small one, but despite the complexity, no significant bug was discovered after finishing the mass coding, not a single one, none. It was constantly stable and reliable. Any time we touch the code in a TDD project, we could leave it in better shape. The quality does not degrade over time. The speed of coding does not drop at the end, we can keep our productivity the same as we started.
  2. Doing TDD is fun as hell! Every programmer knows how it feels to create something new. It is a joy playing around with the fresh product like a kid, and standing up saying “now it works”. The programmer is the new superhero. At that moment we don’t really think about the downside, the upcoming great effort to push it through the QA. During TDD we can have this feeling 20-30 times every day: write the test, it fails, write the code, it passes. “Yeah, I won, it works.” Not only we just wrote a piece of code which works, but also we know for sure that it works perfectly without having side effects. It is even cooler.

What about, side effects?

Of course we could cause side effects any time, we are still humans. The big difference is that instead of not knowing about it for a few weeks, we face it immediately. As soon as we check the fresh code against the pre-created test, we realize that it does not even reach the new test, but fails on a quite old one. Wow, that's a direct hit on the brain! A moment of truth, when we face how easy it would have been to penetrate the code base with a tricky bug. In other words, it’s a “thanks God we have TDD” moment. It is a must to have quick feedbacks in order to improve any skills.

Well, we covered one of the two assumptions, the question of “why we don’t need double effort to implement TDD”.

Part 2 will deal with how to do it in an embedded hardware – firmware project.

We Are Here for You

ScreamingBox's digital product experts are ready to help you grow. What are you building now?

ScreamingBox provides quick turn-around and turnkey digital product development by leveraging the power of remote developers, designers, and strategists. We are able to deliver the scalability and flexibility of a digital agency while maintaining the competitive cost, friendliness and accountability of a freelancer. Efficient Pricing, High Quality and Senior Level Experience is the ScreamingBox result. Let's discuss how we can help with your development needs, please fill out the form below and we will contact you to set-up a call.

We use cookies to ensure that we give you the best experience on our website.