Test-Driven Development (TDD)
Test-driven development (TDD) is an evolutionary approach to development which combines test-first development where tests are developed first based on the requirement before writing production code, which specifies and validates the code and if any deviation is found from the requirement, make the developer add or refactor the code to pass the test case.
The primary goal of TDD is a specification and not validation. It is important both for agile requirements and agile design techniques. The primary purpose of TDD is to write clean and defect-free code.
Certain to make TDD right
- Test code quality: Test quality should be good, If the tests are of poor quality, they will be unable to identify hidden defects and eventually becomes a burden rather than being of use.
- Testing on the right level: Tests should test the behavior of objects, not their implementation.
Checklist of testing
- To be tested with valid inputs
- To be tested with invalid inputs
- Incorporate error conditions and exceptions that can be raised from the code
- To be tested on the boundary condition
Test-driven development process
TDD cycle revolves around five simple steps, which are repeated throughout the software development life cycle. The goal of these steps is to ensure that code should remain simple and efficient while fulfilling all functional business requirements.
- Add Test– Test should define a function or refactoring done on any function in the testing framework. This makes the developer focus on the requirements before writing or modifying the code.
- Run all tests and see if the newly added tests are getting failed– This ensures that the new test does not pass without requiring a new code because the behavior already exists.
- Write some code– Write code based on the requirement specification or modify existing code based on the existing defect or change request.
- Run all tests again and Refactor code– This ensures that the new code added is working as per the specification as well as it is not impacting any of the existing functionality. If any failure found during re-run, then code has to be refactored again.
- Repeat– With a new test is added all the processes are repeated again.
Levels of Test-Driven Development
There are two levels of test-driven development
1. Acceptance TDD (ATDD)
With ATDD a single acceptance test is written which fulfills requirement specification or satisfies behavior of the system and then code implementation is done which successfully satisfies the test. It focuses on the behavior of the system and is also known as Behavior Driven Development (BDD).
2. Developer TDD
With developer TDD a single developer test( i.e. unit test) is written and then code implementation is done which successfully satisfies the test. It focuses on small functionalities of the system and is also known as Test-Driven Development (TDD).
ATDD and Developer TDD combined together
Benefits of TDD
- Early detection of defects- Defects are found at a very early stage which causes significant reductions in defect rates in a testing environment
- Quality code- The code quality becomes very high as it would have better design and also verifies that the requirement is met.
- Refactoring code opportunity- Since any changes made in the code can break the existing functionality which could easily be identified through automates unit test cases, this gives them the confidence to the developer to modify existing code as it will inform if the last change (or refactoring) broke previously working code.
- Decrease overall development time- While total lines of code increase (due to the extra lines in tests) but testing prevents bugs and catches bugs earlier in the process, preventing them from solving later in the stage thus reduces the development time.
- Maintainability – TDD creates code that is maintainable, flexible, and easily extensible.
Pitfalls of TDD
- Tests could be hard to define, especially beyond the unit testing level.
- At the initial stage, it seems like slowing down the development process as it needs to begin, starting from thinking about the interfaces, write the test code, and run the tests before you can finally start writing the code.
- Sometimes the design is not clear at the start and evolves as the implementation goes along – this will force to redo the test which will generate a big-time loss.
- The test suite itself has to be maintained continuously whenever the requirement gets changed which would eventually lead to code change.
- It is hard to apply to existing legacy code.
- Unless everyone on the team correctly maintains their tests, the whole system can quickly degrade.