Test Driven Development (TDD) Step by Step Part 1: Introduction

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 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 details 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 product in 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:

1. 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.

2. 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.

3. Developer’s confidence: With TDD, developers are more safe to change the system as any inappropriate changes will make some tests to fail. With non-TDD system developers needs to take more care to change 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).

4. 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.

5. 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.

6. Better way to fix bugs: In TDD approach when QA finds a bug, developer first writes a test that generates the bug (that is the test will fail due to this bug). Then developer modifies 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.

7. 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 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 bunch of works 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 relaxing mode to learn and practice new approach.

Misconceptions about TDD

Some people argue that TDD takes more time than 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 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.

Share

About the Author

Sohel has more than six years of experience in professional software development with extensive involvement in Web based Object-Oriented, Multi-Tiered application design and development. He's Familiar with Test Driven Development (TDD) and refactoring techniques as well as having expertise in architecturing large enterprise applications. He has Experience in working with Content Management System and Portal Management System tools like SharePoint, DotNetNuke, Ektron.

Over last few years, he’s involved in development with projects on Microsoft SharePoint and received Microsoft MVP for SharePoint Server Development in the year 2011 and 2012. Currently he's working in a software company located Copenhagen,Denmark on a project integrating SharePoint and SAP. You can read his popular blog at: http://ranaictiu-technicalblog.blogspot.com

What you decribe in points 6 and 7 is what Jeff Atwood described in one of his blogposts as Exception Driven Development. The development is not driven by a test, rather you create a test driven by a bug (an exception) the code is already there.

I do agree with the practice. Whenever you get a bug report: first reproduce it by a test and then fix the bug. I think this approach is possibly the smallest and easiest step a developer can make to get acquainted with Unit tests, and is imho a best practice for any company regardless of the applied development paradigm.

It may just be my imagination but TDD just appears to be a compensation for bad development practices. IE I don’t write good requirements, so my program comes out like trash. I think that if good requirements are written, they are testable. I’ve always written requirements with testability in mind. When I’ve had a team available, QA would be involved from the beginning writing test plans. I think design and development should be driven by requirements, not the test module as I also believe the test plan should be derived from the requirements and not the coded results. Way too often, requirements are poorly defined (if at all), there is no organized thought to Architecture or design, and we just fly by the seat of the pants defining things as we go. In this environment TDD would be an improvement. Of course I’m probably a little old school, but it works. Thanks for your effort and please continue the article.

Few points you have mentioned important. Write requirements with testability in mind.QA team's involvement with test plan. But where requirements are poorly defined, introducing TDD may force them to define requirements more elaborately and involvement of QA team will make it much more easier.

I agree. I think the root of the problem is poorly defined requirements. So if a tool/method can help improve the requirements, I'm all for it.We just had a bad experiance this last year with a vendor that wanted to do 'Extreme' programming. He was supposed to be done in 6-8weeks. 8 months later we fired him and built the system ourselves. He never fully understood the requirements (he didn't use TDD either). I have the following sign up in my cube (not sure of the source):Software Requirements: The hardest single part of building a software system is deciding precisely what to build. No other part of the conceptual work is as difficlut as establishing the detailed technical requirements, including all the interfaces to people, to ;machines, and to other software system. No other par of the work so cripples the resulting system if done wrong. No other part is more difficult to rectify later.

I was responding to your previous response - 'The root isn't only 'poorly' described requirements but actually change in requirements during the development.'I was refering to what I thought was generally accepted Software Engineering practices, you need to know 'what' you're building before you can build it. Change management and managing user expectations is part of that. I also don't see how you create test plans for something if you don't know what your building. So that being said, 1)I'm not against automated testing, I'm actually for it,2)It sounds like your trying to capture requirements by writing test cases which is better than what some developers are doing.We just had a bad experiance with an external company doing 'extreme programming'. They never understood the problem, they never understood or documented the requirements, and never developed a usable product.

And to better answer your current questions (I hope), I believe (probably old school again ) change management applies to the requirements level. The Requirements drive everything. If I can't manage changes to the requirements, then I probably will never deliver a product. Testing should be also driven by the requirements wither it is automated or manual. If a bug is found, it generally is a coding issue and not a functional issue. If it is a functional issue it should be properly documented in the requirements and then a test case written to test the specific issue. If it is a coding issue, update the test case.

I was primarily commenting on TDD (and you mentioned EDD) that it seems to be capturing the requirements using test cases. Not my preferred method.

Actually I was just pointing out the difference's between EDD and TDD and that it isn't because you create tests that guard you from regression bugs that you are actually practicing TDD.

I agree that software development is driven by requirements and I personally am trying out BDD (behaviour driven development sometimes called executable requirements), which is a somewhat different approach. Indeed tests based on requirements (I wonder how you came to that conclusion from my messages). And yes I also agree that all changes should first be reflected in requirements and then be developed. In my book however requirements equals tests (in a perfect world anyway ). They can be BDD-tests or User Acceptance tests.

The reason (again imho) Agile practices emerged is because requirements gathering equals extensive analysis in most software teams. A lot of projects got over-analyzed where analyzes had lost the ROI. Nowadays, you can clearly see there is a counter-counter movement against agile development stating that 'code is over-tested', where testing has gone beyond the point of ROI.

TPatterson wrote:

Testing should be also driven by the requirements wither it is automated or manual. If a bug is found, it generally is a coding issue and not a functional issue. If it is a functional issue it should be properly documented in the requirements and then a test case written to test the specific issue. If it is a coding issue, update the test case.

Actually, I think we agree on this, in both cases a test-case exists. However, I don't agree with the statement of your first post:

TPatterson wrote:

It may just be my imagination but TDD just appears to be a compensation for bad development practices.

Whether the problem in 'poor requirements' or 'changing requirements' myy understanding is that TDD can help in both cases.

Poor Requirements: TDD, in a way, forces users to write tests which is actually reflects the requirements (considering requirements are properly documented as in that part TDD can't help). So if with TDD implementation is from requirements and the final goal is to meet the requirements.

Changing Requirements: In case of changing requirements, user can modify code more bravely as he can take for granted that making any improper changes will fail the tests.

- TDD does not seek to replace the test procedure and QA's role of verifying the requirements for the final application. They should still create their own independent test plan to verify the application.

- TDD is a method used by the developers, to build the code up from the requirements, specifications, designs, or whatever architectural plans that are in place before coding starts.

- The final output from the TDD process is a discrete object, and a test harness that verifies the correctness of that object. This test harness can then be used as a regression test for future changes.

- The discrete objects will be combined to create components; The components can be combined to create a system.

- TDD can be used in both of thse phases as well. The key concept to remember, is these are tests developed and maintained by the software engineers.