A little background
Let’s talk about one of the many methodologies of Extreme software testing and development. Toyota Production Systems rings a bell? Well it should since Test-Driven Development was primarily invented as a methodology by Taiichi Ohno, an engineer at Toyota Motor Corporation. His brilliance and obsession in elimination of wastes in production led him to inventing TPS, the mother of TDD. His idea was to create only the real-deal amount of car parts that were absolutely necessary on the pre-production processes. It also required high quality of the pre-produced details and elimination of defects. The communication flow between parts of the operations is being achieved via Kanban AKA sign board.
But that’s all car stuff, so how does it affect development of new software? The catch is in eliminating waste (useless effort) that can be actually crucial for some projects. Here is a splendid scenario of such waste: you work hard, you don’t sleep well, you’re daydreaming of code, more code and so on and so on. Then, after hard work the code is finished and what happens next? It does not get integrated for long days, weeks and sometimes even month. This scenario leads us to difficulties in receiving actual feedback and making the code right. And it’s impossible for it to be correct from the beginning, as we know from the Murphy’s Law (any code has at least one bug, if the program consists of one line of code only there will be a bug in it and so on). Or there can be code written without a reason. Well, there sure was purpose while the writing process was going on, but after all that effort there is but no need for that code. At least not right now so the time could have been spent in fairly more productive way.
That’s where TDD takes its place with a flowing unit test as its righteous Kanban and turning the stairs we all love and are used to around and making sure no wastes (inefficient code) are written. And feedback is provided in time as a pleasant bonus.
Easy in theory
TDD has to be fairly simple due to the fact it has but two rules, yet is all so nice, as they say it is? Let’s get acquainted to the rules of Wonderland where up is down and down is up:
- Forget about writing new code at least until automation has failed you
- Do not repeat yourself! Duplication needs to be disposed of.
Not so hard, are they? Yet they are easier said than done, they create complexities, behave all weird and funky with technical consequences.
If we are to follow the first rule (and we are to, we’re all flaming-keyboarded hardcore TDDsers, right?) then we are to write tests even before getting to the first symbol of the future coding masterpiece. I believe everybody wanted to conquer the stars at a certain period of his life, so let’s use Java to investigate a space ship parameters in our little example.
Let the fact that there are but a few lines of actual testing code and not a single symbol of production code included not bother you, this is but an example. What was done here is specification of some design decisions.
- Creation of the object. Our space ship was created now and its primary state is 0m/s. That’s a rather pour definition of a spaceship but I am not a space craft creator for once and this is what is needed for this example for two. Let’s deal with one issue at a time or, as TDD developers call it lets’ do the organic design.
- API designing. Unit tests will be the lucky ones to be amongst the first to experience the code and they will give us immediate feedback even earlier than the code itself begins its existence. The feedback will be on how easy is it to put the API in usage.
- Decoupling. For now the spaceship class is being decoupled from specific driving models. All it’s aware about for now is the Driving Mode interface. Here we can see (I’m feeling myself as a tourist guide in a museum, while writing these words) a standard TDD pattern. The line containing the design principal is being programmed to an interface rather than to implementation. Plus now we can control the unit that is being tested via the test stub. I’d also rather avoid relying on concrete classes. They usually are adding at least one more uncertainty factor and more than one is not what I like handling in TDD.
- Side-effects are specified. What does shifting the driving mode mean? It means that it will now be equal to top speed of the current mode, as specified in the example.
The start test-case for class is destined to be the one with the biggest amount of exploration. Now, that we have it we can begin writing the spaceship version 1.0.
There are cases, unlike the one above, in which the implementation is not at all obvious. Then you should begin with stub implementation in which hardcoded value needs to be returned. Why to do so? Mainly due to the fact it will guarantee that we are testing the proper thing. It is relevant because with the body growth it is getting harder and harder to be sure proper parts are being tested.
Exploration of alternatives is becoming easier as well. Due to the fact that not too much effort was put to the purpose of writing tons of mega-code it is psychologically easier to just erase all and start all over if necessary. So now let’s run the test, enjoy the green bar and lurk around for duplications as well as possible improvements.
What’s not to my liking in the code from the example is the speed method that is dependent of state. We also have a complication because the speed is held in two various places, thus the conditional is not needed. So I’d try factoring it via taking all the Driving Mode has to offer.
After that the test has to be run again to ensuring that my favorite summer-grass colored bar is being displayed in JUnit (which we were using, and I forgot mentioning earlier, sorry). Now we have a regression test running.
Iterate over and over
These tiny iterations are the basics of the TDD. Taking big steps is an extremely shared mistake amongst developers that chose the TDD path. This part requires balance because if you go too small the entire effort will be simply pointless, yet, on the contrary, if you rush too much all the important feedback TDD provides will be missed, thus the effort will be even more useless. Yet with the small (properly small) steps you are sure to find the problem with ease when a small, yet noticeable unit test is failing.
Small steps will most certainly keep you on the right path as well. You do get constant calls, mail or your co-workers are knocking on the keys too hard and too loud. Quite disturbing, right? Yet with a small test case it won’t be a problem to get back to work after some distractions. It is also a splendid way to finish your busy day at work. A tiny failing unit is not much of a big deal, yet it is a great reminder of what to focus on, when you get back tomorrow.
Unfortunately I find it impossible to provide precise proper steps size. This will depend on specific experience and knowledge of programmers.
Various levels of design
How much up-front design is needed? Yet one more question impossible to answer, due to the fact all depends on the project. Let’s take software for the spacecraft we’ve been working on so hard as our example. Let’s imagine that due to security efforts one of the crafts safety technics was written by two separate teams. They were writing the same programs that are supposed to work in parallel and the parameters have to be between both the programs and some points defined earlier. If the programs decide to disagree the launch will be aborted. This diversified method points out that it is rational to define main design up-front which is two separate programs that will need to agree with each other, and that is hard enough to achieve with but one program.
What tests should I write if I don’t know the requirements?
Hum, how do you know what code to create, when programming? This is the appropriate answer to the question above IMHO. Yes there are tons of questions about various requirements, yet should the unit tests be bond to them? TDD’s answer is a definite ‘no’.
The thing is that requirements tend to come from the issue domain, and we, my fellow explorers, are attempting to enter the domain of solutions during our design journey.
What TDD is trying to offer you is the approach which includes caring over the details but not getting to involved and trying to minimize the plane of details. In other words, write your unit tests in the same programming tongue as the production code was written.
What’s all that TDD for?
What is TDD? I’d prefer to think of it mostly as of a design technique. There is no doubt, of course, that unit test developers do serve an extremely essential verification goal. Yet, you will have to agree that they verify the correctness of the code. Thus almost any project will force them to do the acceptance and requirement testing. Unit testing is but a strong basis that is relevant for the entire process to function properly. That’s how TDD is helping in focusing on important goals with in a way that is extremely more valuable and successful.
As one more positive effect TDD is causing we may take the fact that it is a great intent verification tool. With TDD you will have to state the intent twice in both unit test and the production code and only if they fit you will see the joyful green bar.
Covering the code
Code Coverage is a handy and easy to learn technique. It is used for providing additional feedback on the unit test quality. The catch is in building the analysis of the covered code into the build system. Thus a unit test can be run and a coverage report can be received with but one command. I’d also rather you don’t give too much attention to analysis until at least one version of any module is finished.
The pleasant thing is that usage of TDD will give you 100% coverage (in theory), due to the fact that you are not writing code, as stated in the first rule, until automation has failed you.
So the Code Coverage main purpose is educating you, granting more knowledge. And help in detecting Broken Windows we will get to shortly.
The Broken Windows
At first I’d love to notice that this part has no relation to any operation systems whatsoever. This is a pure reference to psychology. There is a statement that is saying that if you brake one window-glass in a building and it won’t be fixed for a long period of time all the other windows in a building will be broken as well.
What does this have to do with IT? I guess some of you already know where this is headed. A class with absence of a unit test can be called a broken or crashed window and all it eventually does is giving more excuses not to perform more unit checks. And it’s only getting from bad to worse.
It will be hard to fix the window due to the fact that the code was probably not designed with regards to testing.
What will we have on the other side, with tea and cookies? The ability of extending and/or debugging a program that already exists and was “TDD-ed” from the very beginning. So you can just continue writing tests smoothly and moving along. You know the value of the whole window and now you can get a little regression test suite spices as well. It will be very noticeable if code will be written and won’t be test covered. That would be a very loud first window breaking – hard to miss that.
Maintenance of software
Maintaining software will always be a pain in the neck, yet Test-Driven-Development can make it hurt a little less. Thanks to the tiny and lightning-fast interactions your software is in its maintenance mode practically from the very beginning.
How to make modifications successfully? The TDD is sure that you simply need to follow the course, in other words, never to change unit test and unit at the test at the precisely exact time. Let them take turns, if changes are required. The correct flow would be:
- Initial analysis of required changes
- Turn on the unit tests
- Write more and more of those
- Try to understand what needs to be changed an follow the exact same TDD procedure you are used to
Test-Driven Development Recommendations
As we now know TDD is a fairly strict methodology thus making itself very easy to slip from. So here are some things one should never forget if desires to complete the real-deal TDD with success.
- Unit code and the code which is being tested need to have the same quality
- Unit test need to be written small and independent as Monaco, for example
- Use constant naming – it will be easier to navigate if the unit tests will be named appropriately to the units they are testing
So what have we learned?
Yeah, sure Test-Driven Development ain’t the panacea of modern software development and it does not make any other, more traditional phases look outdated and useless. Expect for, maybe, desperate, running-around-like-crazy pre-release bug hunting, but I would call that a phase. It’s rather an unplanned activity with rather entertaining purposes if you ask me.
What it actually does is reversing the coding & testing order thus providing the golden middle for detailed design and splendid feedback. One more important thing you should never forget is that TDD requires extreme discipline and dedication.
Yet it is as always only up to you to choose your path in programing, I simply wish you luck and humbly hope this was informative and gave you some thinking material.
One more thing. Is it all honey? Certainly not as none of software testing methodologies are perfect, so the disadvantages of TDD such as listed below will be covered in the next article. Additional Complexity
- Large time investments
- Impacts of design
- Continuous tweaking
- Other minuses I will be able to cover.