Introduction
There are a lot of software development methodologies. The most popular approaches of the present day are Iterative and Agile. But there are also techniques that are rarely used in their entirety but developers make use of some of the advantages from them in the development process. I think the most well know is Test Driven Development (TDD in what follows).
Testing is a necessary step during software development. Test teams test user interfaces, the business logic of the application, they even can test some parts of the API, using test scripts or applications. But they are unable to do the clean test of the program modules and the program source code in general. This task is usually done by developers. They debug the code and create test applications to test some parts of the code. Today these methods of testing are a little bit out-of-date. Nowadays it is more convenient to write Unit tests (small tests to test individual methods or parts of the source code). There are a lot of Unit test frameworks for different programming languages. Starting from the most popular: .Net (NUnit, MsTest), Java (Jtest, Junit) to the less widely used, like Fortran (Funit) or Haskell (Hunit). In this article, all of the examples will be Microsoft .Net related, because the widest experience in Unit Testing is related to C# .Net development.
But what is the relation between TDD and Unit testing? The answer is the following, TDD is a development methodology based on Unit Testing. After creating an application architecture, after designing all the modules and classes, a developer creates a separate unit test project (the better practice is creating an individual test project for each module) and writes a test method for each logical part of the planned source code. Usually, this means the creation of test methods for each method (for example database layer: for all the entities to create, update and delete them) or for the business logic workflows like making a credit card payment or post the order to the tracking system. The main target is to cover the whole source code by unit tests so that all the code will be tested during test running. There are special tools to calculate the Unit test coverage in the project. For the MS .Net and .NUnit framework, such a tool is NCover. It runs all the unit tests, and checks which parts of the code are covered (it could check if if-construction and switch-construction cover all conditions).
However, this tool could only be run after the source code is written. At the first step, we only have the skeleton structure of the application project and the unit testing project with the complete list of implemented unit tests for all the test cases. “WRITE THE TEST BEFORE THE CODE IS WRITTEN” is the main idea of the TDD and it could make almost all the developers go crazy. So that’s why TDD is so rarely fully used in the development cycle; it’s very difficult to predict what logic should be tested in future code. The easiest part, where the TDD could be fully used, is Database Layer. But there is also one issue: Unit tests must not relate to external resources like the Database or internet connection. Because if it relates, the problem with the external resource (like the slow internet connection) could fail the test and we won’t know the real source of the test issue. So for the Database testing, a detached database is created. In .Net MS SQL projects developers usually create a database structure in an MDF file and attach this file during the testing project, as if it is a real database hosted on the MS SQL SERVER. There should be no difference in db connection for the application in such a case. But for different parts of the code, there are unmanaged resources that could not be detached, like Web services. On the one hand, we could not communicate to the real web service during Unit Testing, on the other hand, we could not “detach” the web service. So we should somehow emulate the work of the web service. There is a special term like the “mock” object for an object which simulates the work of a real object. And there are frameworks to work with such mocks. For .Net NUNit such framework is NMock. I could tell you more about the mocks and the method of changing the real object to the fake ones, and I intend to do this in a separate detailed article.
Stories from real life
I think this is enough theory for now and you probably want to know more about my experience of using the TDD on real projects.
I was involved in writing unit tests for many projects. But only two of them had something that was looking like real TDD.
One of them was a very powerful and scalable Web Based ERP System, designed using the MVC pattern. And the architect of the project (also the tech lead) decided to try the TDD methodology from the beginning. I was involved in Database layer design, so I started the development by creating dummy tests for every entity in the database model. Three tests for each: Select, Update, and Delete tests. In the future additional tests were added for the methods with filter expressions. And I want to say that the complete coverage of the Database layer by the unit tests helped us a lot. It was very easy to track changes in the database. If something was changed (deleted of the added column, or renamed fields) the test just failed. And the developers’ team knew that some additional update scripts for the database should be written so the database created from the initial script could work with the existing code.
For that project, the services layer and the controller layer were covered by the tests and the coverage was almost 100%. For sure it was not the ideal TDD, because a bigger part of the tests was written only after the code was stable. But anyway it was the first and unforgettable experience in writing code that will be automatically tested on a regular rational basis.
On the second project, the situation was almost the same. The only difference was that the tests were not written from the beginning. But after several years, when the project was already in production, the customer changed their representative to an ex-developer. And he suggested to us an idea of the Unit tests Coverage creation, and after a few months the coverage was more than 50%. We also adopted the requirement to add the HTML version of NCover report to every build that was planned for release.
During these two projects, the automatic Unit tests running was configured on the build server and was triggered on every source code commit. So if a developer had committed the code that didn’t work the whole team received the red “panic” mail alert with the build breaker name. So he immediately started to find the problem source to make the fix and commit it, to keep the version on the source control stable.
Conclusion
After all this detailed information and stories from the real-life I want to make a little summary.
So what are the advantages of the TDD?
- We tested all the parts of the code before releasing the build (every developer could run the unit test, when he or she wants on his or her own computer, to know if the code still works)
- We integrated unit tests on the build server to know if the code is working on each build
- We knew who made a bug at the time of the source control commit when the unit test run was triggered to run in each case
So we will have guarantees that all the code is working in case of 100% unit test coverage. This is an optimistic scenario, and the application could have defects even if the unit test check is passed successfully. For example, if a unit test has a bug in it, or developer changes code logic but didn’t change the unit tests. Unit tests should be kept in the actual state or they will just cause problems and frustration during test execution. But even partial use of TDD for development projects could decrease the quantity of the defects by several times. And developers will have much less headache from bug fixing. So I advise every developer to use the TDD and Unit Testing if they have such possibilities and for all projects starting from tiny to the major ones. And you will not regret the time you spent on unit test development, because they will bring a powerful instrument of code testing, that will increase the quality of your project and save a lot of effort in the future.