Practical approach to Unit Testing

This article explains some key approaches that you would consider when writing Unit Tests. 

As developers most of us write Unit Tests and we all know what is a Unit Test. Some may have slightly different opinions, but it is actually a programmatic verification of a single method/unit to ensure it behaves as expected. The reason I explain this because some developers still refers to a Unit is a component, assembly, or a module. When to comes to the term Unit Testing, you really testing a sensible testable unit of your software, and anything bigger than that would considered to be more of a integration testing. 

Benefits

Lets get real and discuss some of the benefits. I want to be more practical, and leave the MSDN Unit Testing benefits behind.

As a developer when you decide to write Unit Tests, do you ask the question from yourself, why I’m really writing these Unit Tests? Some of you answer Yes, and some of you say No.

Answer No, is simply because not knowing what you do, or because everyone else write Unit Tests. If your answer is Yes, that’s great, and you really consider the pros and cons of having Unit Tests.

All these benefits can be categorized into 3 main areas.

1.       Quality.

2.       Design.

3.       Cost benefits.

 

Lets talk about these areas briefly. 

The quality that provides by the Unit Tests can be many ways. It assists developing testable code. With TDD, the method/function that you write against a Unit Test, is well defined, and has a single responsibility. Unit Tests it self provide a valuable documentation to the project.  Less bugs will be introduced, also fixing bugs will be lot easier. As a developer you will have a great confidence on your system. 

If you practice TDD, you know Unit Test allows you to design a specific behaviour within your method. For example with TDD, you write your test first, make it fails, and then based on your test you write the method to ensure that your test passes. The result of this is basically designing a specific behaviour within your method. The other aspect of the Design is, when you design your system, you will also consider your functional requirements. Because one of the things that you need to ensure when you design your system is that your functional requirements have been met. Your Unit Tests are based on these functional requirements. I will discuss this in more detail in “Selective Unit Testing” section. 

We would love to develop a perfect system. But in reality, that is not always the case. Worse and the painful thing is some time projects are over the budget. This could be due to number of factors. One of the most common issues associated with the project cost is unexpected work at the end of the development cycle. This could be due to the number of bugs that have been raised during system testing. The number of iterations between the development and system testing cycles seems never ending due to the amount of bugs been raised. This can be due to poor system design, and or un-testable code. 

As you can see here, the number of defects you find is high during the development and the cost to fix those defects is relatively low when comparing to further stage of project life cycle. This is why the Unit Tests are important to ensure that we introduce fewer bugs during the development so the cost to fix those bugs are negligible at the later stage of the project cycle.

Selective Unit Testing

Writing “Unit Tests” is a good thing. Writing bad Unit Tests and/or unnecessary Unit Tests can lead to considerably high maintenance cost to your overall project. Do you believe on statements such as

We should aim for 100% code coverage”, “Any Unit Test is better than none”, or “Our main priority is the Unit Testing”?. All these statements do not add much value at all. These are some examples of expressing the way of the excitement, which causes by the Unit Testing

Few years ago we hired a developer to one of our projects. He is very keen on Unit Testing and he spent lot of time writing and managing Unit Tests. At the end our project was over the budget, and not what we really expected. If he decided what is really important, and write only the necessary Unit Tests, it would have reduced our cost. 

This is where it is really important that we need to ensure we write the absolutely necessary Unit Tests. This is also applicable if you practice TDD. You just don’t need to write Unit Tests for every method you write with in your application. Lets see some of these characteristics, and then how you decide what Unit Tests to write. 

If you are aiming to write Unit Tests for every method to make your manager happy with higher code coverage, then it is fantastic to write these types of tests. But did you know that most of your tests did not add much value to the quality of your system at all. You see your entire tests pass with green ticks. There are so many green ticks make you feel good. Someone could argue that “All the tests are there to ensure that every method behave as expected when it runs”.  Well not necessarily correct. Did you consider if you have to make a change to a component how much re-factoring need to be carried out? 

Let’s explain this with an example. When you have an obvious method, which takes a String parameter and returns a Boolean, and there are 100 methods calling this simple method, change to this method to return a String would require changing all those 100 methods. How about the repetition? Your Unit Test probably written to ensure that calling method takes a String value and returns a Boolean. Isn’t this repeating the same thing? How about the DRY? The time you have spent to write this Unit Test, you should have spent for some important task such as analysing some business requirements. 

Consider the future maintenance cost for both production code and all these Unit Tests? The amount of time you need to write Unit Tests for every single method, the amount of time you have to spend to write tests for complicated methods, which add no obvious benefits to the design of the system. They do not add any value at all. 

You may also reluctant to make changes to your code because of the amount to Unit Tests and the fear of breaking them.

Decisions 

So how you decide what Unit Tests to write. In order to decide this, you need to understand the types of methods you would normally write within your application. 

1.       Simple methods – These methods are very obvious and simple. Just by having a look at code you can clearly see what it does.  The code it self explains the design of it.

        /// <summary>

        /// Gets the first name from fulllname.

        /// </summary>

        /// <param>The full name.</param>

        /// <returns>First Name</returns>

        public string GetFirstNameFromFulllname(string fullName)

        {

            if (string.IsNullOrEmpty(fullName))

            {

                return string.Empty;

            }

            return fullName.Substring(0, fullName.IndexOf(” “));

        }

If you perform additional design and verification through Unit Testing, this method will give you a minimal benefit.

So if the method is simple and the design is obvious, you don’t need a Unit Test. If you are a TDD partitioner, the concept is the same. You do not need to write tests for every method you write within your application. 

2.       Components that are already tested – I see some developers trying test components that have already been tested by the framework.            

        /// <summary>

        /// Adds the companies to cache.

        /// </summary>

        /// <param>The company list.</param>

        public void AddCompaniesToCache(IList<Company> companyList)

        {

            if (companyList != null && companyList.Count() > 0)

            {

HttpContext.Cache.Add(“companyCahchkey”, companyList, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 15, 0), CacheItemPriority.Normal, null);

            }

        } 

The real purpose of this method is to add a list of companies to cache. A test would be to ensure the list is added to the cache. Why would do you need to write Unit Tests against this method when the ASP.NET caching – Cache  is already tested for its capabilities. You can also see this method is very simple and hence Unit Tests are not required. 

3.       Complex methods and have no value to the design at all

For example you may have a method that writes to a registry using a complex pattern, and then using a complex algorithm to generate some custom strings, then serializes the data before calling a web service.

It will be very exhaustive exercise to write dependencies for some of these complex methods. There are so many things and variations to consider. These methods are not related to your main design or requirements but to assist or help other methods to get the job done.  Most of these methods are self-contained algorithms. 

4.        High Level methods – There are methods within your application that integrated or calls other Units of methods. 

public void ManageResourceData(string resources)

        {

            if (string.IsNullOrEmpty(resources))

                throw new ArgumentNullException(“resources”);

            string retResourceValues = MergeResources(resources);

            SaveResourcesToAFile(resources);

            EmailReaources(resources);

            AddResourcesToDatabase(resources);

            SetUserPermission();

        } 

As you see here there are few units of methods being called. Writing Unit Tests for these types of methods, often waist of time. 

5.       Complex methods with so many dependencies.

Some refer to this as GOD methods. Most TDD practitioners not experience these type of methods as they would to like to perform one assert per single method. Also methods are smaller and always written for a single responsibility. You might come across with a situation where complex method with so many dependencies. The method itself performs so many other things and need to be re-factored in order to make it more testable. You can see these methods really hard to Unit Tests. You have two options

 a.       Re-factor your method and consider using interfaces, so you can mock all your dependencies during Unit Test.

b.       Simply do not waist your time to write the Unit Test. You may not have enough time to re-factor, and you can spend this time for something more useful.

Unit Tests

So the next important question is “What types of methods that you write Unit Tests against?”. The answer is not simple, which why you need to think carefully, before you write Unit Tests.

 If you an application author, you would consider writing Unit Tests against the method that actually define your requirements. The focus should be to write methods with less number of dependencies by providing a single responsibility, while focusing on a specific requirement. Your dependencies should be easily mockable. The same consideration is applied if you are practicing TDD. When you implement your method with, then to pass your Unit Test, you should consider it as defining a specific requirement. If not, the test it self does not add value to your app.

Requirements

The above graph displays the number of Unit Tests increases with the requirements. The more requirements you have the more Unit Tests you should have. 

For example you may have a high level requirement to write a Banking application where customer performs banking operations. When you design your application, you will consider more detail requirements, such as customers should be able to withdraw, deposit, transfer, print statements within your application. Regardless of wether you are TDD practitioner or not, you should consider writing Unit Tests for withdraw, deposit, transfer and print statements. By doing this you add the real quality to your application. You will have the confidence that your application performs as expected from the Unit Level.

You should not consider writing Unit Tests for any algorithms that may have to calculate your balances, encryptions, serializations, locked/isolated, or wether the database get initialised. These are some of the examples that you should avoid as they do not add much values your Unit Tests.

If you framework author you would consider writing Unit Tests ensure that the methods you have written confirms to the key design principles of your framework. Framework author should target the requirements, which provided by the design of the framework. For example, you are developing a resource management framework, and you need to ensure the developer who uses the framework could successfully retrieve images from your resource management system. So you would write a Unit Test to ensure that developers can retrieve images as expected.

I do not say that we should avoid Unit Tests and should not practice TDD. They both provide some real benefits to the application. However having unnecessary code / Uni Tests leads to more maintenance, and extra cost to projects. 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s