Test Driven Development: Best Practices and Benefits of Using Unit Testing on Real Life Projects
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 the software development. Test teams test user interfaces, 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 conventional to write the Unit tests (small tests to test individual methods or parts of the source code). There are a lot of Unit test frameworks for the 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 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 make 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 tests coverage in the project. For the MS .Net and .NUnit framework, such 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 test must not relate on the 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 a 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 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 techlead) decided to try the TDD methodology from the beginning. I was involved in Database layer design, so I started the development from creating dummy tests for every entity in the database model. Three tests for each: Select, Update and Delete tests. In 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 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 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 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 automatically 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 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.
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 tests 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 changed code logic but didn't change the unit tests. Unit tests should be kept in actual state or they will just cause problems and frustration during test execution. But even a 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 advice 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 tests development, because they will bring a powerful instrument of the code testing, that will increase the quality of your project and save a lot of efforts in future.