← Back to overview
April 10, 2010 · NUnit Unit Testing Ninject

Unit testing your App.config and ConfigurationSection using NUnit, Ninject and TestDriven.NET

Intro

Let me just make things clear. This post is not about creating mocks.
This post is about really unit testing the different scenarios you have when working with configuration files:

To get started you need the following:

NUnit is the testing framework we’ll use to perform our unit tests.
Ninject will be for dependency injection.
TestDriven.NET will be used to run the tests.

And I presume you have a basic knowledge of unit testing and dependency injection.

Preparing the application

Ok, first we’ll create a command line application that reads the App.config and displays the contents.
Result:

In brief, this is how the application will work:

  1. There will be a ConfigurationSection mapping the fields with the App.config (ServerConfigurationSection)
  2. This section implements the IServerConfiguration interface
  3. Then we’ll have the ServerConfigurationProvider, this class is responsible for reading the configuration file and throwing exceptions if something goes wrong.
  4. The provider implements IServerConfigurationProvider that will be registered in our DI container.

Creating the ServerConfigurationSection

So first we’ll create the ServerConfigurationSection responsible for mapping the fields:

 public class ServerConfigurationSection : ConfigurationSection, IServerConfiguration
 {
    [ConfigurationProperty("server", IsRequired = true)]
    public string Server
    {
        get { return (string)this["server"]; }
        set { this["server"] = value; }
    }

    [ConfigurationProperty("port", IsRequired = false)]
    public int Port
    {
        get { return (int)this["port"]; }
        set { this["port"] = value; }
    }
 }

 public interface IServerConfiguration
 {
     int Port { get; set; }
     string Server { get; set; }
 }

As you can see we have 2 fields, Server and Port.
Port is an optional field.

Now that we have this section, we can create the following App.config file:

 <?xml version="1.0" encoding="utf-8" ?>
 <configuration>
   <configSections>
     <section name="srvConfig" type="Sandrino.MyApplication.ServerConfigurationSection, Sandrino.MyApplication"/>
   </configSections>

   <srvConfig server="sandrino.loc" port="1986"/>
 </configuration>

Creating the ServerConfigurationProvider

Now that we have a ConfigurationSection we can go ahead and create the provider.
Our provider will be read the App.config using the mapping available in the configuration section.

This is the base class that can be reused to quickly create new providers:

public abstract class ConfigurationProviderBase<TSection, TException>  
    where TSection : ConfigurationSection
    where TException : Exception, new()
 {
    /// <summary>
    /// Name of the section to read from the app.config.
    /// </summary>
    private string sectionName;

    /// <summary>
    /// Config object used to read a custom configuration file.
    /// If not set the default file will be used.
    /// </summary>
    private System.Configuration.Configuration config;

    /// <summary>
    /// Initialize the provider.
    /// </summary>
    /// <param name="sectionName"></param>
    public ConfigurationProviderBase(string sectionName)
    {
        this.sectionName = sectionName;
    }

    /// <summary>
    /// Set a custom configuration file.
    /// </summary>
    /// <param name="config"></param>
    public void SetConfigurationFile(string file)
    {
        // Create the mapping.
        ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
        fileMap.ExeConfigFilename = file;

        // Open the configuration.
        config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
    }

    /// <summary>
    /// Read the configuration file.
    /// </summary>
    /// <returns></returns>
    protected TSection Read()
    {
        // Try to read the config section.
        TSection section = GetSection() as TSection;
        if (section == null)
            throw new TException();

        // Done.
        return section;
    }

    /// <summary>
    /// Get the section from the default configuration file or from the custom one.
    /// </summary>
    /// <returns></returns>
    private object GetSection()
    {
        if (config != null)
            return config.GetSection(sectionName);
        else
            return ConfigurationManager.GetSection(sectionName);
    }
 }

What this class does in a few words:

Now to use it for our ServerConfigurationSection we’ll create the ServerConfigurationProvider:

 public class ServerConfigurationProvider : ConfigurationProviderBase<ServerConfigurationSection, ServerConfigurationMissingException>, IServerConfigurationProvider
 {
      public ServerConfigurationProvider()
          : base("srvConfig")
      {

      }

      /// <summary>
      /// Read the configuration and just return the interface.
      /// </summary>
      /// <returns></returns>
      public new IServerConfiguration Read()
      {
          return base.Read();
      }
 }

 public interface IServerConfigurationProvider
 {
    IServerConfiguration Read();
 }

As you can see we just inherit from the ConfigurationProviderBase and just return the interface IServerConfiguration instead of the ServerConfigurationSection.
Why? I’m not going to go into "What is a loosly coupled architecture", but if we do it this way the reference to System.Configuration (and the assembly) stays minimal.

Creating the application

First we’ll need to make sure our application knows that IServerConfigurationProvider == ServerConfigurationProvider.
Later on we’ll see why we also abstract the provider to an interface.

First we’ll use Ninject to bind the provider to its interface.
Any other dependency injector will also do.

 public class ConfigurationModule : StandardModule
 {
    public override void Load()
    {
        Bind<IServerConfigurationProvider>().To<ServerConfigurationProvider>();
    }
 }

Finally we can go ahead and create our command line application:

 class Program
 {
    static void Main(string[] args)
    {
        using (IKernel krn = new StandardKernel())
        {
            // Create bindings.
            krn.Load(new ConfigurationModule());

            // Get the provider and read the app.config.
            var prov = krn.Get<IServerConfigurationProvider>();
            var config = prov.Read();

            // Display.
            Console.WriteLine("Server: {0}", config.Server);
            Console.WriteLine("Port: {0}", config.Port);
            Console.Read();
        }
    }
 }

Again, if you can’t read the comments:

And shazooom… it works.

Testing the configuration

This is where it gets interesting.
Now we’ll need to test the possible outcomes when reading the configuration file.

Setting up the test project

First of all we’ll need to create a new project of type class library.
We also add a reference to Ninject, NUnit, our main project.

And finally we also create multiple configuration files.
Each configuration file will have something different (a value missing, the section missing, …) to cover each possible scenario.

Now, before writing the actual tests.
Remember I said that we’ll see later why ServerConfigurationProvider was also abstracted to theinterface IServerConfigurationProvider?

Because, using Ninject we can do some pretty cool stuff.
Look at this:

 private void RegisterConfiguration(string filename)
 {
    krn = new StandardKernel(new InlineModule(m => m
        .Bind<IServerConfigurationProvider>()
        .ToMethod<IServerConfigurationProvider>(
            (e) =>
            {
                ServerConfigurationProvider config = new ServerConfigurationProvider();
                config.SetConfigurationFile(filename);
                return config;
            })
        )
    );
 }

In english this would be:

And in the method we declare we just create a new configuration provider and pass it our custom filename.
But the actual tests and event the classes that could use the provider are not aware of this.

The great thing is, maybe next year you’ll move to a registry based configuration.
You’ll just need to create 2 new classes (maybe ServerRegistryProvider and ServerRegistryConfiguration) implementing the correct interfaces.
After that you’ll just need to update the bindings and your whole application stays untouched.

Writing a test

First we have the configuration file:

 <?xml version="1.0" encoding="utf-8" ?>
 <configuration>
  <configSections>
    <section name="srvConfig" type="Sandrino.MyApplication.ServerConfigurationSection, Sandrino.MyApplication"/>
  </configSections>

  <srvConfig port="1986"/>
 </configuration>

As you can see, the server field is missing. But this is a required field! Ooh-ooh!
Let’s create a test for this scenario:

 [Test]
 [ExpectedException(typeof(ConfigurationErrorsException))]
 public void ServerMissing()
 {
    RegisterConfiguration("Configurations\\ServerMissing.config");

    // Read the file.
    var prov = krn.Get<IServerConfigurationProvider>();
    var section = prov.Read();

    // Assert.
    Assert.IsNull(section.Server);
 }

And there you go. We register our provider to use ServerMissing.config.
After that we’ll try to read the configuration file and it will raise an exception!

Finally you can run the test by right clicking on method:

Download the complete solution: Sandrino.MyApplication.zip (128.87 kb)
It contains a few tests for different scenarios.

Article also available on CodeProject.

Enjoy!

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket
Comments powered by Disqus