Sunday 15 May 2011

Test Driven Development (TDD) a critical review of the claimed advantages gained by using this technique.

Test-driven development (TDD) is a software development process concerning the creation of concise, clean and testable code. The core principle of TDD asserts that testing should be done as part of the development process to drive the software’s progression (Beck, 2004). More specifically, tests should be created for isolated functionality prior to the implementation of code for that functionality (Erdogmus, Morisio, Torchiano, 2005). The TDD approach guides developers along a series of iterative steps to optimise the development and testing processes. The first step stipulates that a simple test must be established for an isolated requirement. Such a test will inevitably fail to compile due to the absence of production code. However, the process continues with the development of code to enable the test to compile; producing a fail result. Once the test compiles, the production code for the requirement in question can be fully implemented, so as to pass the test. The final stage of the TDD process involves the refactoring of both the test and production code in an attempt to reduce duplicate code and ensure that the existing design is optimal (Beck, 2004). In addition, as the code is refactored, the test should be continually run to guarantee that the code continues to behave as expected. This cycle is then repeated for every required function to continue the development process. For each function the developer must create a test, get that test to fail, write code to pass the test and then refactor the implemented code, whilst ensuring that the test, and all previously established tests, are still passed (see Figure 1.1 for a simplified diagrammatic explanation).

The question naturally arises, what functionality should be tested? I.e. how complex should each test be? In answer to this, TDD dictates that tests should be as simple as possible, focusing on a discrete behaviour. The principle being to ensure that the each behaviour is tested in isolation (Astels, 2003). Subsequently, TDD implies that tests should be designed to minimise dependencies. Methods or components outside the context of the behaviour in question should not be considered by the test. This therefore, ensures that the developed section of code behaves as expected in isolation (Janzen, Saiedian, 2008). With regards to the definition of the boundaries surrounding a particular test, design patterns known as testables (such as mocks, stubs and fakes), can be utilised to isolate the behaviour being tested, ensuring the test is kept as simple as possible (Astels, 2003).

Thus far, this paper has considered TDD to be a development process employed at the genesis of a project to ensure the program code is testable from the bottom up. However, TDD can also be applied part way through a project, or indeed to legacy code. Under such circumstances, TDD can be utilised to great benefit, via the refactoring of existing code and the introduction of numerous function specific tests (Linnamaa, 2008). However, the implementation of TDD to an existing project does generate additional risks, particularly if the code being altered has no pre-existing testing procedures. Nevertheless, to deliver the benefits associated with TDD, this technical debt must be paid.

The main justification behind the TDD process is that it guarantees the testability of production code, since tests are used to guide the creation of the software. Naturally, this guarantees extremely high code coverage is achieved since all of the code is testable (Linnamaa, 2008). Subsequently, this implies that the developed code will be clean and concise, since to isolate code for testing it is typically necessary to ensure that the code is simple and well written (Beck, 2004). Additionally, by creating the test first, TDD places emphasis on the specific behaviour in question and not the interface or outside behaviour (Martin, 2007). This therefore further encourages the development code to be less complicated and well written. Also, the process of testing first encourages good program design, in that developers are required to think about the code from a testability and task orientated perspective right from the beginning (Beck, 2004). Before the code is written the developer must assess why the code is being created and what result must be returned. This helps to retain focus in the task at hand, but also helps to document the code as it is written since tests provide a working documentation for the developed code (Ambler, 2008). Moreover, the principle of testing as the code is being developed assists programmers in detecting and fixing bugs early in the projects life cycle since tests provide instant feedback concerning the behaviour of the implemented code (Erdogmus, Morisio, Torchiano, 2005). Similarly, any attempts to alter the code, or optimise its design, can easily and quickly be tested. Thus, if the existing program logic gets broken, it can quickly be detected and resolved (Beck, 2004). As a result, TDD greatly reduces the regression testing time since the testing procedures are already in place (Ambler, 2008). This therefore could dramatically reduce the project costs by minimising the time spent on the testing phase of the programs lifecycle. Additionally, the high code coverage and vigorous testing instils confidence in the quality and accuracy of the program code; the code is tested as it is developed, thus ensuring it works as intended (Erdogmus, Morisio, Torchiano, 2005). In this respect, TDD acts as a psychological tool for developers, guaranteeing that the software works (Linnamaa, 2008). Furthermore, TDD has the advantage that it introduces a great deal of quality into the codes design right from day one since testable code requires components to be loosely related, thus guaranteeing an agile and extensible structure (Natté, 2009).

However, there are also a number of disadvantages associated with TDD procedures. Most notably, the use of TDD can greatly slow the development process by imposing strict testing procedures (Erdogmus, Morisio, Torchiano, 2005). Subsequently, the costs associated with the project can be significantly escalated due to the delay in development. Additionally, TDD places a significant burden on the developer in terms of maintaining the numerous tests since each test will require maintenance as the code being tested develops and changes (Linnamaa, 2008). This requirement, of continuously tweaking the test code to accommodate for changes in the program code, further delays the development process. Moreover, many projects evolve during the course of their development; at the beginning of a project the solution is not always foreseeable. Consequently, developers will be forced to redo tests, creating additional time delays (stackoverflow.com). Furthermore, many dependencies between classes and methods need to be broken to create testable code using TDD. For larger, more complex projects this breaking of dependencies to isolate individual test cases can be extremely difficult and may in-fact add to the overall complexity of the project (Erdogmus, Morisio, Torchiano, 2005)

In summary, it is clear that there are a number of distinct drawbacks which can arise from the implementation of TDD, particularly with regard to the amount of time invested in the development phase. Nevertheless, despite these undesirable characteristics, TDD offers a logical and structured design approach, forcing developers to focus on the task at hand. Implicitly, TDD encourages developers to produce simple, maintainable code. TDD is therefore an extremely effective development style, helping to ensure the development process remains structured and agile.


APPENDIX:


Figure 1.1:



REFERENCES:

S. W. Ambler, (2008), Introduction to Test Driven Design (TDD), AgileData, accessed on the 24.11.2010, http://www.agiledata.org/essays/tdd.html

S. W. Ambler, (2007), Test-Driven Development of Relational Databases, IEEE Computer Society, Vol. 24, No. 3, p. 37 – 43.

D. Astels, (2003), Test-Driven Development: A Practical Guide, Prentice Hall PTR.

K. Beck, (2004), Test-Driven Development: By Example, Pearson Education, 5th Edition.

H. Erdogmus, M. Morisio, M. Torchiano, (2005), On the Effectiveness of the Test-First Approach to Programming, IEEE Transactions on Software Engineering, IEEE Computer Society, Vol.: 31, Iss. 3, p. 226 – 237.

D. S. Janzen, H. Saiedian, (2005), Test-Driven Development Concepts, taxonomy, and Future Direction, IEEE Computer Scoiety, VOl. 38, ISS. 9, p. 43 – 50

D. S. Janzen, H. Saiedian, (2008), Does Test-Driven Development Really Improve Software Design Quality? IEE Software, Vol. 25, no. 2, p. 77 – 84.

L. Linnamaa, (2008), Test-Driven Development, University of Helsinki Computer Science Department,http://www.cs.helsinki.fi/u/linnamaa/linnamaa-Test-Driven-Development-final.pdf

R. C. Martin, (2007), Professionalism and Test-Driven Development, IEEE Computer Society, Vol. 24, No. 3. p. 32 - 36.

M. Natté, (2009), Introduction to Unit Testing, .Net blogs, accessed on 26.11.2010, http://martijnnatte.wordpress.com/2009/07/09/introduction-to-unit-testing/

stackoverflow.com, accessed on 26.11.2010,

http://stackoverflow.com/questions/64333/disadvantages-of-test-driven-development

No comments:

Post a Comment