Test driven development (TDD) is an agile practice that has been introduced by Kent Beck, or as he puts it “rediscovered” in his book “Test Driven Development in Practice” [Kent, 2002]. Its structure is simple although a bit controversial because of the way it affects the development process. TDD is represented by a development circle, starting always with writing tests and then followed by writing the implementation code.
How it works
Indeed, the first thing is to make a list of the tests that you have to write in order to achieve the goal of your software. The idea behind this, is that it makes you think about the requirements and the specifications of the software before you write any code. Next step is to run these tests and get the “red light”, meaning that those tests should fail. This will ensure that the tests really work and validate the test harness. Therefore, having the tests in place, the following step is to write some code. Since you broke a test you need to fix it! Moreover, TDD suggests writing the simplest code possible, just to pass the tests in place. Particularly, Kent Beck [Kent, 2002] suggests to “Fake it until make it” which implies even returning constant values in order to make the test succeed. As a result, you will never implement something that you are not going to need (YANGI) but you will implement “just enough” to pass the tests. Once the tests and the implementation are in place you need to run the tests and get the “green light”, meaning that all code must pass the tests. Now you need to step back and think about it. You’ve started with failing tests in the morning, you wrote the least possible code at lunch time and in the evening you have a fully working and tested program. Doesn’t this makes you more comfortable? Doesn’t it make you feel more productive? Apparently, that is the point. Of course, you don’t feel that comfortable because even if your code passes the tests you know that you forced it to. For this reason, the next step is to refactor the code. This is the time where you have to apply your favorite software quality standards and principles, while having already the design and the documentation in place, which makes it a lot easier. At last, since you’ll make the tests run again, the implementation always implies new tests. Due to that, you have to go again from step one and follow the development circle.
As can be seen from its definition, the benefits of TDD are numerous, from software design to developer’s psychology, and from software requirements to testing and high coverage. Personally, I believe that this technique can often lead to a more reliable software. To begin with, when a feature is finished, most of the time managers force developers to move fast to the next one since the first one “works”. However, the feature is not fully tested which eventually affects its safety. Thus, using TDD you ensure that your feature is already tested with high coverage. Other than that, TDD significantly improves the design. As Ward Cunningham mentions “Test first coding is not a testing technique”. Moreover, Kent Beck presents [Beck, 2001] that TDD offers more than just simple validation of correctness, it can also drive the design of a program. TDD is thus an analysis and a design technique since writing tests before the code requires to consider the design of the solution. In my opinion, this is a good technique to avoid the “analysis-paralysis” effect. It forces you to think about the design, which is considered a topdown process, but during writing tests, which is a bottom-up process. Consequently, it introduces a middleout process which can positively affect the tests, the design process and the developers productivity.
So now that your confidence and the test coverage are high and your designs are good, it is time to mention that TDD does not always work. As presented in the previous paragraph, TDD “ensures” reliability, but apparently not always. This is why, most of the time the same developers who write the tests, also write the code. Because of that, the possibility of leaving “blind spots” in the implementation is very high, since the developers are “biased” with high confidence towards their code correctness. The developers think that everything has been tested and feel comfortable about it. However, there are a lot techniques to solve this problem. For instance, test coverage frameworks that analyze to which extent the code has been tested, or random testing that checks cases that you never thought about. In addition to that, there is always the case where the requirements were elicited the wrong way, and in this case you have a perfectly working and tested software, but the wrong software. Furthermore, as it can be seen [Oram and Wilson, 2010] [Beck, 2001] TDD is not applicable in cases that require a lot of functional testing, for example in GUI testing. Additionally, there is no recommended best context for the use of TDD. Thus, TDD can not be used as a framework for specific cases and, when applied, it can not always ensure that the best option, since TDD is difficult to learn. It involves a steep learning curve that requires skill, maturity and time [Oram and Wilson, 2010]. From my experience, you need to invest a lot of time in order to learn TDD, and even some more to practice it before applying it. Generating some personal “heuristics” and patterns that will make your work-flow easier will help. Despite that, once you grasp it, it is easily applicable and provides you with all the aforementioned advantages. Eventually, if not TDD itself, some of its variants, help a lot on many difficult situations. One of those TDD variants is Acceptance Test Driven Development (ATDD). Whereas TDD is a tool for well written unit of code, ATDD is a communication tool for requirements that are well defined. I believe that applying ATDD is a good practice since it does not affect the quality of your code, it is not time consuming, most of the times at least, since you usually test only the APIs, and it can make sure of the validity of the requirements, something that is a problem in the case of TDD. I personally use ATDD a lot. Following ATDD there is also BDD, Behavioral Test Driven Development that focuses on tests which describe behavior. Empirically speaking, there is evidence [Oram and Wilson, 2010] which suggests that on one hand, TDD strongly favors external quality and the modularization of an application, but on the other hand it negatively affects the productivity. Because of the steep learning curve, in the beginning it decreases productivity and introduces a lot of initial overhead. However, I believe it that despite that, it significantly increases productivity overtime, because of the end result is both running and tested software.
Test Driven Development is a very controversial topic in software process. Its costs are usually unknown which makes it an ambiguous technique to use. Online you can find fighters from both fields, favoring or opposing TDD with numerous success and failed stories. In the end it is a nice idea which provides great insights. As agile suggests, “adjust”, try it, and adjust it to your work-flow. If it does not work, change it.
[Beck, 2001] Beck, K. (2001). Aim, fire. IEEE Software, (5):87–89.
[Kent, 2002] Kent, B. (2002). Test driven development: by example. Boston, Massachusetts: Addison-Wesley Professional.
[Oram and Wilson, 2010] Oram, A. and Wilson, G. (2010).
Making software: What really works, and why we believe it. ” O’Reilly Media, Inc.”.