Introduction
So at first, let’s ask ourselves what is Test Driven Development (TDD)?
“TDD is an evolutionary approach to development which combines test-first development where you write a test before you write just enough production code to fulfill that test and then refactor the code to pass the test”.
TDD is an upside-down approach for non-TDD developers. What’s our natural approach to development? We write code to implement a functionality in mind. Once we finish coding, we test then modify if an error is found and test again. This cycle continues. This traditional non-TDD approach is shown below:
Image 1: Traditional Approach to Software Development.
On the other hand, in TDD approach, you write test first rather than production code. So in reality, with TDD even if you don’t have a class OrderProcessSystem
(considering you are working on an order processing system), you are writing a test for making sure Order Processing System is working fine just taking for granted that the class OrderProcessingSystem
exists. It might sound bizarre. But it's the main point in TDD. You want your test to fail at this point. Then go create classes, add functionalities and make sure the test passes. The overall approach is “You write the tests first, watch them fail, and then start producing the code that will get them to pass”. I’ll show you this in detail later in this series of posts. The TDD approach is shown below in the following figure:
Image 2: TDD approach to software development.
Why TDD? What’s the Problem with Traditional Approach?
Now you may ask, hey man, I am not using TDD but my development is going well. I‘m delivering the product on time with quality and bla bla bla. To answer your question, let me remind you first “No process in this world is the best in all cases”. Every process has its merits and demerits. Even if nowadays waterfall model is appropriate for some kind of systems. So my focus is not TDD is best over non-TDD approach. Rather TDD offers some sort of benefits that we can leverage:
- Automated testing: Nowadays as system is getting bigger and complex, people are moving to automated testing. Automated system makes the testing much more easier and faster. Manual testing is slower and needs human interaction which is error-prone. Automated testing makes sure any particular test is not missing every time we test the system, whereas human may leave a test out mistakenly during testing process.
- Better to test New/Modified Functionality: With manual testing, whenever you add new functionality or modify existing functionality, QA personal needs to test all systems that may be affected due to the modification which may be time-consuming. This may also leave a hidden bug in the system after modification. With TDD, whenever a new functionality is added or existing functionality is modified, the whole set of tests in the system is run to make sure existing tests are passed. This makes sure that existing functionality is not broken.
- Developer’s confidence: With TDD, developers are more safe to change the system as any inappropriate changes will make some tests fail. With non-TDD system, developers needs to take more care to change the existing system as the new change may fail other functionality. But with TDD, developers can do so easily as after modification if developer runs the test, he/she will find immediately that the change has failed some other tests (i.e., some other part of the system).
- Manual testing stage is shortened: In non-TDD development, as soon as developers prepare a build, QA personal starts testing. This manual testing takes a reasonable amount of time and increases the time to deliver the product from the build time. With TDD, a major part of the system is tested during development and needs lesser QA involvement in the time between build and final delivery of the product.
- Alternative Documentation: Unit tests are kind of documentation to system. Each unit test tells about an individual requirement to the module or system. For example, the following test ensures that only a logged in user with proper balance can buy an item when the item exists in stock. This test reflects a user aspect scenario of the system.
public void CheckUserCanNotPurchaseItemWithoutBalance()
{
LoginToTheSystem();
SelectAnItemToBuy();
MakeSureItemExsitsInStock();
MakeSureUserHasBalanceToBuy();
RunTheTransaction();
}
- Better way to fix bugs: In TDD approach when QA finds a bug, the developer first writes a test that generates the bug (that is the test will fail due to this bug). Then, the developer modifies the code to make sure that the test is passed (i.e., the bug is fixed). This approach makes sure that next time the bug will not appear in the system as a test is written in the system to take care of the bug.
- Repetition of the same bug reduced: The way bugs are fixed in TTD is described in point 6. With TDD, once a bug is found, it's put under test. So every time you run the whole test in the system, the tests associated with the bug are run and make sure the bugs are not generated again.
Are You Convinced with TDD?
May be reading the above section, you are convinced that TDD is the best as it offers so many benefits. From my personal experience, before introducing any new approach, you need to know:
- What skills the new approach expects developers to have?
- What are the restrictions/demerits of the approach?
- Will this approach fit with your system or environment? How can you fit TDD in your team?
Let's answers these questions.
Skill set requirements: With TDD, the developer writes test first rather than code. Many developers find it difficult to follow this approach (and most of the time resist not to follow TDD) as they are used to write code first. Most of the time, non-TDD developers don’t want to follow this “test first then write code” as this is the upside-down way of developing to them. Also in TDD, the whole dependency is on Test case. TDD considers that test is the right thing and we need to make sure the test passes. But what if the test itself is not correct or enough tests are not written to cover all aspects of the system. If your test doesn’t cover all aspects of the system, then maybe you are missing some parts of the system to test. This is the case with manual testing and we are using automated testing to make sure the whole part of the system comes under test. So writing good test cases is important for making successful TDD system. But if you don’t start and practice writing tests, you don’t know how to write good tests.
Restriction of TDD: TDD requires having good skills on writing test cases which cover all aspects of the system. But writing good test cases requires to introduce TDD and practice more and more to be expert in TDD. So initially after introducing TDD, you may not get the full benefits of TDD until you have a good number of people in your team having good skill in writing TDD. Also TDD can’t cover all aspects of the system. For example, you can’t test UI, multithreading, etc. So even including TDD, you need to have manual tester. Sometimes, you need to maintain tests as well as code to make sure that tests reflect the system requirements. This is an extra overhead for TDD.
How to Introduce TDD in your team: Once you are convinced with TDD, you’ll try to Introduce TDD in your team. But you need to be careful and need to consider the following aspects before introducing TDD.
- Sometimes, some good initiative fails due to the way initiative is taken or introduced. For example, you are overwhelmed with TDD and then decided to force all members in your team to follow TDD. From my point of view, this will introduce a catastrophic productivity loss of the whole team. First of all, you need to consider that your team members need to be expert in TDD to get full benefit of TDD. So introduce TDD in a small scale. Ask 2/3 developers to follow TDD. Once they get used to TDD, ask few others to join with them. This way after few months, you will find that your whole team is following TDD.
- Sometimes, you’ll find few members of your team are not interested in TDD. Don’t force them to follow TDD. Rather use TDD with others who are interested. If you can prove TDD is better, then once the guy who resisted to follow TDD will come and follow TDD.
- Select the proper time to introduce TDD in your team. For example, you have production release next month and you have a bunch of work to do. Introducing TDD at this stage will slow the productivity of the team down. So select time to introduce time when you have less work pressure and team is in a relaxed mode to learn and practice the new approach.
Misconceptions about TDD
Some people argue that TDD takes up more time than the non-TDD approach. This may be true when you are introducing TDD in your team and your team members don’t have expertise in TDD (i.e., how to write test cases). Remember in the long run, your team members will be expert in TDD and you’ll get the full benefits of TDD.
May be the above argument will not satisfy you as you will argue that writing TDD will take time which we could save if we wouldn’t write test case. This is true that writing tests takes extra time but it also saves time from other ends. Without TDD, you need to depend fully on manual testing. In non-TDD approach you use to run the code, debug to test or find bugs. So with non-TDD approach, you spend most of the time on manual testing. Also with non-TDD approach, reasonable time is spent with QA personal to test the system. But with TDD approach, you spend more time to write test cases but less time to run-debug and also less time with manual testing. So my argument is that with TDD, your testing time is finally reduced as developers don’t need to depend heavily on manual testing and as testing with QA personal is shortened.