Ugly Stool Rotating Header Image

February, 2012:

Test Configuration Sections in .config Files

The .config files centralize and standardize how per application or per library configuration data are stored.  The format can be a little dense to get through, and I always tend to leak this information from my brain.  I only need to know when I start a new project. 

I have had to relearn configuration data yet again, so I figured I should commit it to the electrons so I do not forget again.

Use the tests with external file dependencies post as a starting point.  The tests have to be written this way because the System.Configuration API does not support accepting input from a stream.

Step 1: Create a minimal configuration.  In this case, I have two configuration values.  The maximum number of tasks, and the period.

namespace My.Library
{
    public class MyConfiguration : ConfigurationSection
    {
        [ConfigurationProperty("maxTasks", IsRequired = true)]
        public int MaxTasks
        {
            get { return (int)this["maxTasks"];  }
            set { this["maxTasks"] = value; }
        }
        [ConfigurationProperty("period", IsRequired = false, DefaultValue = "00:00:15")]
        public TimeSpan Period
        {
            get { return (TimeSpan)this["period"]; }
            set { this["period"] = value; }
        }
    }
}

Step 2: Create an example configuration file to use for testing.

<configuration>
  <configSections>
    <section name="myConfig" type="My.Library.MyConfiguration, Library" />
  </configSections>
  <myConfig
    maxTasks="1"
    period="00:00:01" />
</configuration>

NOTE: When defining the configuration section (<section />) at a minimum the type attribute must include the full name of the type (My.Library.MyConfiguration).  The value after the comma is the assembly name (minus .exe/.dll) containing the type.  Additional information can be specified such as the version, and public key token, i.e. strong name of the assembly.

Step 3: Write a test to validate the configuration file.

[Fact]
public void MyConfiguration_OverrideAllValues()
{
    string fileName = Assembly.GetExecutingAssembly().LoadFileAssemblyResource(
        "My.Tests.examples.example1.config");
    try
    {
        ConfigurationFileMap fileMap = new ConfigurationFileMap(fileName);
        Configuration configuration = ConfigurationManager.OpenMappedMachineConfiguration(
            fileMap);
        MyConfiguration mine = (MyConfiguration)configuration.GetSection(
            "myConfig");
        Assert.Equal(1, mine.MaxTasks);
        Assert.Equal(TimeSpan.FromSeconds(1), mine.Period);
    }
    finally
    {
        File.Delete(fileName);
    }
}

This test validates the configuration data loaded matches the data in the seed file, example1.config.  There are two other tests that should be written.

  1. Validate that period correctly defaults to 15 seconds.
  2. Validate that MaxTasks is a required parameter.

Tests with External File Dependencies

Testing a system where you have to reach outside the environment can be awkward.  A piece of code that requires a file name instead of a Stream is bad, and to be avoided.  While this can be enforced when using one’s own code, it is not true when utilizing third party code.

If you have to test an interaction with file data there are several ways to handle it.

  1. Explicitly copy the file in the test.
  2. Use the test framework to ensure the file is deployed with your test.
  3. Embed the test data in your assembly, and recover the file data at test time.

I favor option the last option because I think it offers the most flexibility.  The first option requires you to account for a resource beyond your test and binary bits.  The second option requires you to manage data outside of your IDE, i.e. cleaning up all of those test specific directories.

Step 1: Add the file to test against to the solution.  I typically add mine to an examples folder in the test project.

image

Step 2: Embedded the file in the assembly, so when you build the project the file is packed into the assembly.

To embed the file on build do the following.

  1. Right-click the file to embed.
  2. Select properties.
  3. Under Build Action select Embedded Resource.

image

Step 3: Create an extension method to extract the resource data to a temporary location on disk.

public static class MyExtensions
    {
        public static string LoadFileAssemblyResource(this Assembly assembly, string resourceName)
        {
            string tempFileName = Path.GetTempFileName();
            using (FileStream fileStream = File.OpenWrite(tempFileName))
            using (Stream stream = assembly.GetManifestResourceStream(resourceName))
            {
                stream.CopyTo(fileStream);
            }
            return tempFileName;
        }
    }

I like the idea of an extension method, but it is not required.  Accepting the Assembly as a parameter is necessary because you will probably need to differentiate between assemblies instead of just picking the executing assembly.

Step 4: Write a test that requires the external file, run the test, assert the results, and cleanup the fie.

[Fact]
public void Test()
{
    string fileName = Assembly.GetExecutingAssembly().LoadFileAssemblyResource(
        "My.Tests.examples.example1.txt");
    try
    {
        var lines = File.ReadAllLines(fileName);
        Assert.Equal(5, lines.Count());
    }
    finally
    {
        File.Delete(fileName);
    }
}

NOTE: The name of the file is not used to locate the file in the assembly, rather the name of the resource is.  The resource name is made up of the default assembly namespace, the folder path, and the name of the file all concatenated by a period.  This is why the file is loaded by using the name “My.Tests.examples.example1.txt” and not “example1.txt”.

Page optimized by WP Minify WordPress Plugin