Friday, September 17, 2010

Unit Test Application Configuration Settings

Background

All the post I had written this month so far were related to the Entity Framework learning series. This time I thought of taking a diversion and share a quick method of unit testing setting which might be stored in the Application Configuration file (App.config) of a DotNet application.

 

Unit Test Application Configuration Settings

Lets fire up Visual Studio and create a simple console application. You can name it whatever you want. I have named the project as AppConfigTest. Once the project is created right click on Add and select Add New Item option in the solution explorer window. Select Application Configuration File option and click Add. This will automatically name the new file as App.config.

Add App.Config

Since this file used to configure settings at application level there is a possibility of someone changing these setting unknowingly. It can lead to severe problems later.

It might also happen that the settings are different in different environments like development machines, test systems and production systems. We would not like someone to checkin the source code with developer box settings which might result in breaking the functionality in test or production environments.

To overcome these problems, I thought of writing a test which can be executed as part of the automated build to ensure that the default or the predefined values are not changed by mistake during development.

Lets first write a piece of code which actually accesses some key from the application settings. Assume a hypothetical example wherein the application has some timer functionality. The interval of the timer is configurable and the default value should be 2 minutes. I’ll start by adding a key to AppSettings element.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="DefaultTimerIntervalInMinutes" value="2"/>
  </appSettings>
</configuration>


I have created a key value pair and named the key as DefaultTimerIntervalInMinutes and assigned the value as 2. Lets access this value from our program. In order to access configuration related settings from our code, we need to add a reference to the System.Configuration assembly.



configuration reference



We can make use of the ConfigutationManager class to access various configurations. I’ll get hold of the AppSettings node using the AppSettings property. This is of the type NameValueCollection.



            NameValueCollection appSettings = ConfigurationManager.AppSettings;

            string defaultTimeIntervalInMinutes = appSettings["DefaultTimerIntervalInMinutes"];

            Console.WriteLine("Default timer interval in minutes : {0}", defaultTimeIntervalInMinutes);


Since this is a collection I can access individual items either using the index or the key name of the application setting. I prefer to use the key name based approach as it gives us the flexibility of adding more keys without worrying about its index. If we use the index to get reference to the key value pair, we’ll need to ensure that nobody changes the position of the key in the items collection. I don’t think its practical and try to stay away from that approach.



By mistake if somebody comments the line which defines the default timer value in the App.config the code still builds but when I run it I don’t see any value on the console output. Here the impact is minimal, but if I were to use that value for any calculation the application would have thrown a runtime exception when I try to assign the null value to any other object. To avoid such situation I want to write a simple test which ensures that the value always exists in the configuration file.



2. Create Test Project



I’ll add a unit test project to the current solution. Simplest way to do that is to right click on the Main method and select Create Unit Test option from the context menu.



creta unit test



Just to fool the VS IDE create a new project by giving any valid name. I choose to name the project as AppConfigTest.UnitTests. You can name it anything as per your preference. I said to fool VS because I just want to use the template provided by the IDE. I am going to delete the file which is created by VS and create a new one from scratch. This is just a shortcut which saves me lot of pain of adding references to VS quality tools dll etc.



unit test project



Let the wizard create the project for you. If you check the references it would have added most of the necessary references for us. I’ll go and delete the file ProgramTest.cs because I don’t want to test anything in the Program.cs file at this point.



Lets add a new class to the unit test project and name it as ApplicationConfigurationSettingsTest.cs. I want to make sure that the default value for the interval is assigned as 2. So lets write a test to verify that.



    [TestClass]
    public class ApplicationConfigurationTest
    {
        [TestMethod]
        public void DefaultTimeIntervale_Should_Equal_Two_Minutes()
        {
            string expectedValue = "2";

            NameValueCollection appSettings = ConfigurationManager.AppSettings;

            Assert.AreEqual(expectedValue, appSettings["DefaultTimerIntervalInMinutes"]);
        }
    }


I have TestDriven.Net add inn installed so I can just right click on the test and say run test to execute the test. When I run the above test it fails saying expected 2 but was null. This happens because we are trying to access the AppSettings. AppSettings are specific to a project or the application. These values are taken form the startup project. When I try to execute the tests, the test assembly does not have the App.Config file defined for it. Hence it returns a null object.



There are various ways of solving this problem. The easiest is to add an App.Config file to the unit test project as well and copy the same entries that are there in the actual project that we are trying to test. But the problem with this approach is we always need to keep the two files in sync.



Another approach is to automatically copy the App.config file from the project to the unit test project using the post build events. I feel this again is a bit tedious process. If someone unknowingly deletes the post build event script then the unit tests will fail and debugging would be difficult.



I would like to use a different approach here. I can add a new file to unit test project. But instead of creating a new file, I can link it to an existing file. The advantage of using this approach is that the changes needs to be done in only one place. There is no additional scripting knowledge required to copy files using the post build events as well.



So lets start by right clicking on the unit test project and add existing item option. Navigate to the folder where App.Config file is stored on the disk. You might need to select All Files filter to see all files in the directory. Select App.Config and click on the arrow on Add button and select As Link.



Add link



You can see the difference in the way the file is represented in the solution explorer when it is linked to another file. now lets try running the test once again. Voila, this time the test runs successfully.



In some cases it might happen that even after undertaking these steps the test might fail. You can set the copy to output directory property as Always copy for the App.Config file in unit test project and that should solve the problem.



copy to output directory



I would like to run one more test to ensure that the changes in the original App.config files are really reflected to the linked one and ultimately in the tests. I go and comment the line which defines they key and run the test again. The test fails and confirms that the two files are always in sync.



Conclusion



As I mentioned above I have seen people use different approaches to copy App.config files to the unit test projects. I think this is one of the simples approach. Since these tests are not testing any logic as such purists might argue that they are not unit tests in real sense. I agree and may be these can be run as part of functional or customer tests.



One of the biggest advantage I see in testing configuration settings this way is that any mismatch in the release mode settings can be identified very early.













As usual I have uploaded the complete source code to dropbox which can be downloaded here.



Until next time Happy Programming :)



8 comments:

  1. Anonymous4:34 PM

    It's really helpful

    ReplyDelete
  2. Anonymous6:20 PM

    That definitely helped me. I first thought about creating a script to do that. I then found this article.

    Thanks.

    ReplyDelete
    Replies
    1. Glad that you found this helpful

      Delete

  3. I like your post about "Unit Test Application Configuration Settings" very nice post. It is very help full.I do appreciate about this post & this blog ... :)
    vulnerability assessment
    penetration testing

    ReplyDelete
  4. Anonymous9:58 AM

    I tried all the methods you mentioned in your post and now I tried the linked file approach. It works fine and seems simple enough to implement.

    Thanks for sharing.

    ReplyDelete
  5. It's incredible how allmost no one can write accurate articles on these things. Following your example here, my attempt to use it stops at "The type or namespace 'NameValueCollection' could not be found." So, which references are you skipping here?

    ReplyDelete
  6. Anonymous7:29 AM

    Excellent. This is the best solution I found.

    ReplyDelete

How Travis CI saved my time?

Background Some time back I created an Ansible playbook to install software and setup my Mac Book Pro . I put the code for this on GitHub . ...