DEV Community

Harish Babu
Harish Babu

Posted on • Originally published at chekkan.com on

Faking .NET Framework ConfigurationSection for Unit Tests

Faking .NET Framework ConfigurationSection for Unit Tests

Many are now familiar with using typed configuration that's available in .net core and .net 5 with the help from Options pattern. However, if you are working on a projects targeting .NET Framework, you will know ConfigurationSection from System.Configuration assembly.

ConfigurationSection allows you to group related configurations together in xml. Lets take an example of providing some simple configuring settings to a retry client.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="retrySettings" type="ConfigSectionTest.RetrySettings, ConfigSectionTest" />
  </configSections>
  <retrySettings count="3" delay="00:00:30" failureThreshold="0.5" />
</configuration>
Enter fullscreen mode Exit fullscreen mode

Notice the configSections.section node where we are naming the config section and also pointing to the type where its implemented. retrySettings xml node has various attributes and values assigned for each.

The RetrySettings class is implemented as...

public class RetrySettings : ConfigurationSection
{
  [ConfigurationProperty("count")]
  public int RetryCount => (int)this["count"];

  [ConfigurationProperty("delay", IsRequired = true)]
  public TimeSpan RetryDelay => (TimeSpan)this["delay"];

  [ConfigurationProperty("failureThreshold")]
  public decimal FailureThreshold => (decimal)this["failureThreshold"];
}
Enter fullscreen mode Exit fullscreen mode

With expression bodied property syntax, the class is fairly small. I've specified RetryDelay to be required using the ConfigurationProperty attribute.

In order to use the configuration section RetrySettings, you'd do so by calling ConfigurationManager.

ConfigurationManager.GetSection("retrySettings") as RetrySettings;
Enter fullscreen mode Exit fullscreen mode

Even though you can create an instance of RetrySettings, it is not possible set the values of the properties without modifying the class (providing a setter). You will be able to mock the class by introducing an interface or using an actual appsettings.config file.

However, it maybe easier to create a fake retry settings config section instead of mocking. In order to do this, you can inherit the ConfigurationSection class which gives you access to the DeserializeElement method. This method takes in an xml string which is your section node. The second argument indicates whether to serialize only the collection key property.

class FakeRetrySettings : RetrySettings
{
  public void PopulateConfig(string xmlString)
  {
    using (XmlReader reader = new XmlTextReader(new StringReader(xmlString)))
    {
      reader.MoveToContent();
      DeserializeElement(reader, serializeCollectionKey: false);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

You can now configure the retry settings as needed to orchestrate your system under test.

var retrySettings = new FakeRetrySettings();
const string xmlString = "<retrySettings count=\"3\" delay=\"00:05:00\" />";
retrySettings.PopulateConfig(xmlString);
Enter fullscreen mode Exit fullscreen mode

You can make it much more easy to work with the RetrySettings class in test project by having a builder class.

class RetrySettingsBuilder
{
  private readonly Dictionary<string, string> properties = new Dictionary<string, string>
  {
    { "count", "3" },
    { "delay", "00:30:00" },
    { "failureThreshold", "0.5" }
  };

  public RetrySettingsBuilder WithProperty(string key, string url)
  {
    if (!properties.ContainsKey(key))
      throw new ArgumentException(nameof(key));

    properties[key] = url;

    return this;
  }

  public RetrySettings Build()
  {
    var xmlString = "<retrySettings ";
    var pairs = properties.Select(pair => $"{pair.Key}=\"{pair.Value}\"");
    xmlString += string.Join(" ", pairs) + " />";

    var settings = new FakeRetrySettings();
    settings.PopulateConfig(xmlString);
    return settings;
  }
}
Enter fullscreen mode Exit fullscreen mode

Builder has some default values already configured. Therefore, if you wanted the test to work and you don't worry about the values, you can create a new instance of the configuration section by calling new RetrySettingsBuilder().Build();. You can override the default value by calling WithProperty method e.g. new RetrySettingsBuilder().WithProperty("count", "2").Build();.

Top comments (0)