Unit Testing 101

From Coder's Log

Jump to: navigation, search


Have you ever seen how the car engines are being diagnosed nowadays? There are special computers at the car repair facilities that can be hooked up right to your car. Inside the engine there are several embedded sensors that can communicate with this computer. They transmit information about the engine's conditions to the computer and it tells you exactly what is going on. Isn't it quite amazing?

As a software developer I always wanted to have this special computer that could tell me instantly why my code is not doing what it's supposed to do. Unfortunately, there is no such computer in a real world. However, there is something else that can do pretty much the same job. It is called test driven development. How can you, as a programmer, make sure that your code is functional and meets the client's expectations? What are the ways to verify that nothing has changed in the system’s behavior since the last server upgrade? Can you make a certain change without breaking the entire system? To answer these questions we have several options.

Option number one is to remain ignorant and continue the development process hoping that at the end things will just work. While many people use this naive approach, it is not hard to imagine what can happen at the end. Usually, this would result in a failed project and potentially a request for the HR department to hire a new project manager to replace the one who led the project.

The second option would be to hire a tester - someone whose job responsibility would be to run your code and make sure it behaves according to the specification. While this approach sounds easy enough, it has one serious problem: you can't start testing until your code it written completely, or at least you have enough of it so you can execute it. But, what if you made a mistake along the way and your code simply doesn't run? How can you, as a programmer, quickly find out where the problem is? Using this approach, it is going to be very hard or sometimes even impossible to identify the source of the problem and resolve it in a short period of time. Not only is it going to be very difficult, it is also going to be very time consuming and expensive. Not to mention that once you found the error, there is no guarantee that the code will run. You simply might have another error similar to the one you just fixed somewhere else in the code and you will have to repeat the entire debugging processes over and over, again and again, until all the problems are fixed.

Fortunately for us there is another way. It is called test driven development. The idea behind this approach is to develop a suite of automated tests that can test your code on a regular basis and provide you with valuable feedback regarding the conditions of the program you're writing.

This is how it works in general. First, you develop your tests and only then you write your code. By the time you start writing your main system code you already have a unique custom-built tool that can almost instantly diagnose your system and tell you if there are any problems. When you get to the point where you need to finish your project, your code will be intensively tested from inside out using variety of test scenarios and many of the potential problems will be already identified and removed. And even if a new problem occurs, it is going to be relatively easy to narrow it down and eliminate the source of the problem in a short period of time. Sounds good, doesn't it?

Well, before we go any further we need to define what the unit test is. This is the definition given to us by Wikipedia – a free on-line encyclopedia:

In computer programming, a unit test is a procedure used to verify that a particular module of source code is working properly. The idea about unit tests is to write test cases for all functions and methods so that whenever a change causes a regression, it can be quickly identified and fixed.

Personally, I am not completely satisfied with this definition, that's why I came up with my own:

A unit test is a software module that verifies the results of a single operation or a unit of functionality by emulating a user's request and comparing the generated output with predefined data.

The concept of unit testing is based on the same idea as using the embedded sensors to diagnose your car engine. Each unit test acts similar to those sensors – it verifies the correct execution of one single block of code and reports you whether it worked according to the expectations or not. The more sensors you have inside your code the better it is going to be in the future. They execute your program and compare the generated output with the previously defined data. If they match then test passes, otherwise the test fails and a message indicating the test failure will be displayed. That doesn't look like something really difficult, right? In fact, making sure that your code passes the test is usually much easier than trying to find why your program doesn't work.

Do you remember the old days when you had to take your car to the mechanics and they would spend hours trying to investigate why your car started to consume all this extra fuel? They would always be glad to do all this work for you, because at the end they would charge you for every minute they spent working on your car. The worst part of it was that once they told you they fixed, what they thought was broken, there was really no guarantee that they really fixed the problem instead of introducing a new one. And the next time you would come to them again and spend your time and money trying to get your car back in the shape.

Those of you who have had experience managing or coordinating the software projects will definitely see some similarities. How often do we find ourselves in the situation when we need to make a change to the code and simply can't do this because we are not sure if this change is going to break some existing functionality? How often do we hear from software developers that it will take ten minutes to implement the required changes and another three or four hours (or even days) to complete the full system test? To make matters worse, imagine the situation when one of the core developers just quit the job. How can a new person, who was recently hired, tell if the change that is about to be implemented is going to break something else?

The answer to all this questions is – use automated test driven development process. Do not try to save money on testing your products. In the end, poorly written code represents a serious liability to the clients. It is much like manufacturing untested cars. You would not want to drive a car that never went through a crash test, right? If something goes badly, you might find yourself working on your resume a lot sooner that you had expected.

However, despite the obvious advantages and benefits that test driven development can provide for us, many people still remain suspicious. One of the most often used arguments against test driven development is: "I don't have this extra time required to write tests". Well, it is really easy to beat. All you need to do is ask them – how much time do you have to debug your code? Would you rather spend time and energy on debugging your code instead of writing the unit test? If the answer is yes then this person should probably consider doing something else other than software development.

Another very hard thing to do is to convince your management to allocate additional time and recourses to develop unit tests. This one is a little bit more difficult. Unfortunately, managers and especially those who don't have any technical background often don't see the benefits of writing the unit tests because, in their eyes, it is just another activity that programmers like to do. The best way to convince the management in this situation is to demonstrate in practice, using a real life example, how the test driven development can save time and money and at the same time improve the quality of the product. It is good to start with a small project and use it as a sand box to sharpen the required skills before going into the deep waters and starting to work on a large project. It is going to be a lot easier to receive managerial approval to try something new while developing a small module to enhance the exiting system than on a huge enterprise wide mission critical project.

There are number of tools that can help developers to utilize test driven development. Probably, the most well known is JUnit - a framework written in Java programming language. JUnit provides software developers with a set of predefined objects that can be extended for their purposes. JUnit is an open source project and available for download from its own web site: http://www.junit.org.

Without any doubts, one of the biggest advantages of JUnit is its tight integration with modern development tools. Almost every existing integrated development environment (IDE) for Java including Intellij IDEA, Eclipse, NetBeans, and JBuilder has a built-in JUnit test runner. Another significant advantage that JUnit gives to developers is the ability to run unit tests while building the projects using different build tools such as Ant, Maven, and Cruise Control. For those who can't or simply don't want to use Java there is a .NET port of JUnit framework called NUnit.

There are also many other tools and frameworks that can help developers to design and develop the unit tests for their projects. More information on this subject is available from http://www.junit.org.

Personal tools