I’ve said this so many times, writing unit tests is easy if the code was written well in the first place.  And when I say “written well”, what I really mean is “written to be testable”.

So here is the situation:  I have a simple service that does some logging, most of the details are not important.  But in the LogDebug() method, the code checks the web.config file to see if the debug logging is enabled.  That too in itself is pretty simple stuff.  Typically it would look like this:

bool configDebugMode = bool.Parse(WebConfigurationManager.AppSettings["Logging.Debug"]);

EDIT: I should point out what I mean when I say below that testing this code isn’t very easy. Sure, testing it is easy if I only want to test one value from the web.config file, such as ‘true’. But in this situation, I want to test when the config file value is ‘true’, and also when it is ‘false’.

But testing that isn’t very easy at all.  You’d have to do some crazy stuff with multiple config files or something.  But if the code was written differently, it’s very easy to test.  The key is to wrap the call to WebConfigurationManager in another class that implements and interface, then use dependency injection.  And with the class designed like that, you can use a simple mock to make the testing simpler.  It’s really easy, here is how I’m doing it.

First I define an interface, I’m calling it IConfigurationManager: 

public interface IConfigurationManager
{
    string GetAppSetting(string key);
    ConnectionStringSettings GetConnectionString(string key);
}

The interface has two simple methods so I can use it to get AppSettings and ConnectionStrings.  Could be I’ll need to add some more methods later but I don’t need them yet.  Next I implement the interface in a concrete class that can get the values from the web.config.  This too is really simple.  I’m calling this class WebConfigConfigurationManager.  That may sound weird and redundant but it tells me that it is a ConfigurationManager that gets values from WebConfig.  If I need an implementation that pulls values from a db I could make another one called DbConfigurationManager.  Here is the code:

public class WebConfigConfigurationManager : IConfigurationManager
{
    public string GetAppSetting(string key)
    {
        return WebConfigurationManager.AppSettings[key];
    }

    public ConnectionStringSettings GetConnectionString(string key)
    {
        return WebConfigurationManager.ConnectionStrings[key];
    }
}

So far this is was pretty easy to write and you can see it isn’t too complicated so it should work pretty well.  But this doesn’t make it any more testable.  The key to making it testable is Dependency Injection.  My LoggingService will “depend on” this WebConfigConfigurationManager class.  But instead of just instantiating WebConfigConfigurationManager within LoggingService, I’ll inject it in.  By doing that, I’ll also be able to inject something different during unit tests.  Hang in there, I’ll show you how.  First, here is the injection part, it is much simpler than it sounds. I inject the dependency via the constructor, you’ll note I am injecting a few other dependencies too:

public LoggingService(IEnterpriseLoggingRepository loggingRepository,
                        ILoggingInformationAdapter loggingAdapter,
                        IConfigurationManager configurationManager)
{
    if (loggingRepository == null) { throw new ArgumentNullException("loggingRepository"); }
    _loggingRepository = loggingRepository;

    if (loggingAdapter == null) { throw new ArgumentNullException("loggingAdapter"); }
    _loggingAdapter = loggingAdapter;

    if (configurationManager == null) { throw new ArgumentNullException("configurationManager"); }
    _configurationManager = configurationManager;
}

If you were expecting something fancy, sorry to disappoint you. The Dependency Injection part is pretty simple too.  Here is the LogDebug() method too.  Please don’t worry about the details of _loggingAdapter.ToDomainModel().  That is something I need to turn the “message” into something that the LoggingRepository can work with. 

public void LogDebug(string message)
{
    try
    {
        //are we in debug mode?
        bool configDebugMode = bool.Parse(_configurationManager.GetAppSetting("Logging.Debug"));

        if (!configDebugMode)
            return;

        //convert the message into something the logging repo can use.
        LoggingData data = _loggingAdapter.ToDomainModel(message);

        //log it!
        _loggingRepository.LogDebug(UiSystem, data);
    }
    catch (Exception loggingException)
    {
        // if an exception occurred during logging, capture the message that was being logged in the first place
        throw new LoggingException(loggingException) { OriginalMessage = message };
    }
}

OK the code is written, so how does this make the testing easier?  Since the tough to test code is in a dependency, when I write the test, I can inject something different in.  In this case, I want to inject in a class that I can easily control and return either true or false for the Logging.Debug setting.  There are a bunch of ways to do this, I’ll use a mocking framework to help.  In this case I’m using MOQ.  Here’s most of the code from my NUnit TestFixture (I took out a bunch of extra tests for brevity.  An explanation follows.

[TestFixture]
public class LoggingServiceTests
{
private Mock<IEnterpriseLoggingRepository> _loggingRepository;
private Mock<ILoggingInformationAdapter> _adapter;
private Mock<IConfigurationManager> _configurationManager;
private LoggingService _loggingService;

[SetUp]
public void Setup()
{
    _loggingRepository = new Mock<IEnterpriseLoggingRepository>();
    _adapter = new Mock<ILoggingInformationAdapter>();
    _configurationManager = new Mock<IConfigurationManager>();
    _loggingService = new LoggingService(_loggingRepository.Object, _adapter.Object, _configurationManager.Object);

}

[Test]
public void LoggingDebugIsSkippedIfConfigSettingIsFalse()
{
    _configurationManager.Setup(cm => cm.GetAppSetting("Logging.Debug")).Returns("false");

    _loggingService.LogDebug("This is a test");

    _adapter.Verify(a => a.ToDomainModel(It.IsAny<string>()), Times.Never());
    _loggingRepository.Verify(lr => lr.LogDebug(It.IsAny<string>(), It.IsAny<LoggingData>()), Times.Never());
}

[Test]
public void LoggingDebugIsExecutedIfConfigSettingIsTrue()
{
    _configurationManager.Setup(cm => cm.GetAppSetting("Logging.Debug")).Returns("true");

    _loggingService.LogDebug("This is a test");

    _adapter.Verify(a => a.ToDomainModel(It.IsAny<string>()), Times.Exactly(1));
    _loggingRepository.Verify(lr => lr.LogDebug(It.IsAny<string>(), It.IsAny<LoggingData>()), Times.Exactly(1));
}

If you aren’t familiar with Mocking or MOQ, you may want to read up on the topic.  I’ll try to hit the highlights.  Check out the SetUp() method.  You can see that instead of using “real” classes, I instantiate a bunch of Mocks so I can control them easily.  In the last line of the method I “inject” the mocks into the LoggingService.  Next, let’s examine the method LoggingDebugIsSkippedIfConfigSettingIsFalse().  The first line of code pretty much says when the ConfigurationManager’s GetAppSetting() method is called with the argument “Logging.Debug”, just return false.  I really like the MOQ syntax, if you are used to Lambda expressions, the MOQ stuff reads pretty easily, I think.  The mock class won’t actually look at a config file or anything.  Remember, this is a mock so there is no implementation.  With code like this, you can actually step through with the debugger and see the mocks act as you told them too!  If you look back to the actual LogDebug() method you will see that if the Logging.Debug value is false the method simply ends.  So then the last two lines of code in the test verify that the adapter’s ToDomainModel() and the logging repository’s LogDebug() method are never called at all!  That’s all there is to it.  Plus the second unit test shown here does the opposite, it makes sure that all of the code is executed if Logging.Debug is set to true.

I can assure you it took me a lot longer to write this blog post than it did to code the IConfigurationManager, WebConfigConfigurationManager, and LoggingServiceTests.  I hope you find this code helpful.

9 thoughts on “How to Unit Test Code that uses AppSettings from Web.Config

  1. feel free to use

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Configuration;
    using System.Text;
    using System.Xml.XPath;
    using System.IO;

    namespace Configuration
    {
    ///
    /// Provides an testable ConfigurationManager *lite* based on System.Configuration.ConfigurationManager
    /// using the section.SectionInformation.GetRawXml() member to get sections definition.
    /// Try to store add elemnt information in a namevalue collection
    ///
    ///
    /// var tcm = new TestableConfigurationManager("MyFullname_App.config");
    /// var nvcDlF = tcm.GetSection("saveTheWorldSection");
    /// Assert.AreEqual(12-5, saveTheWorldSection.Count);
    ///
    public class TestableConfigurationManager
    {
    public IDictionary Sections = new Dictionary();

    ///
    /// Stores all existing error free processable @keys @value pairs of add elements from all sections in a configuration file
    /// in namevalue collection according to its section sectionname.
    ///
    /// FullPath of a configuration file
    ///
    /// - missing propper error handling (what if you have identical keys in a configuration)
    /// - missing namespace handling
    /// - unknown whats happens with appsettings
    /// . feel free to make version 1.1
    ///
    public TestableConfigurationManager(string pathToConfigFile)
    {
    //check
    if (!File.Exists(pathToConfigFile))
    {
    throw new ConfigurationErrorsException($"Konfigurationsdatei {pathToConfigFile} nicht vorhanden oder fuer User {Environment.UserName} nicht erreichbar.");
    }

    //read and loop all sections
    ExeConfigurationFileMap customConfig = new ExeConfigurationFileMap
    {
    ExeConfigFilename = pathToConfigFile
    };
    var eSections = ConfigurationManager.OpenMappedExeConfiguration(customConfig, ConfigurationUserLevel.None).Sections.GetEnumerator();
    while (eSections.MoveNext())
    {
    try
    {
    var nvc = new NameValueCollection();
    var section = ((ConfigurationSection)eSections.Current);
    var sectionName = section.SectionInformation.SectionName;
    //reding configuration sections xml
    var csXml = section.SectionInformation.GetRawXml();
    if (!string.IsNullOrWhiteSpace(csXml))
    {
    var docNav = new XPathDocument(new MemoryStream(Encoding.UTF8.GetBytes(csXml)));
    var nav = docNav.CreateNavigator();

    //loop over all add elements in section
    var addIterator = nav.Select(".//add");
    while (addIterator.MoveNext())
    {
    //pupulate NameValueCollection assuming if there is an key you will have an value also
    var key = addIterator.Current.SelectSingleNode("@key");
    if (key != null)
    {
    var name = addIterator.Current.SelectSingleNode("@key").Value;
    var value = addIterator.Current.SelectSingleNode("@value").Value;
    nvc.Set(name, value);
    }
    }
    }
    //store namevalue collection according to sections sectionname
    Sections.Add(sectionName, nvc);
    }
    catch (Exception)
    {
    //todo errorhandling
    //throw;
    }
    }
    }

    ///
    /// Gets Section like System.Configuration.ConfigurationManager would do in an application for its app.config
    ///
    /// Section namne of the section
    ///
    public NameValueCollection GetSection(string sectionName)
    {
    return ((NameValueCollection)Sections[sectionName]);
    }
    }
    }

    Reply
    • I suppose this is ridiculously out-of-date, but it looks something like this, with the test configuration setting in the app.config of the test project:

      namespace DIB.SecureStore.Test
      {
      [TestFixture]
      public class TheConfig
      {
      [Test]
      public void CanRetrieveAppSettings()
      {
      var c = new WebConfigurationManager();
      var s = c.GetAppSetting("ConfigTest");
      Assert.AreEqual("Test Value", s);
      }
      }
      }

      Reply
  2. This is such a good article, the way its explained is outstanding. Writing code like this with testability in mind makes you a better developer.

    Reply
  3. Derek,

    The big difference is that methods with the Setup attribute get run once befor EVERY test in your class. This ensures that everything will be reset and tests don’t effect each other. The ClassInitialize attribute will make sure that your method runs, but only ONCE per class. In this case, you could initialize mock objects here but anything you setup for a mock will effect all of the tests in your class.

    Good luck,
    Andy

    Reply
  4. thanks for the great post!! it was a good starting point for me to quickly get up and running. I am using Moq and Visual Studio 2010. I wasnt able to use the setup() method in my test, there is another auto generated method
    //Use ClassInitialize to run code before running the first test in the class
    [ClassInitialize()]
    public static void MyClassInitialize(TestContext testContext)
    {

    }
    can I use this to initialise the mock config object ?

    Reply
  5. Nick has unfortunately missed the point of this post. Sure, I could have put the setting in the test’s app.config file. But that wouldn’t make testing it flexible. I want to be able to test for a value of "true" or "false" in the config file. Now I can easily do either test by mocking out the object that I call to get the value.

    Reply
  6. Why not just put the same settings in the App.Config file of the Test? The reason I say this is because WebConfiguratonManager.AppSettings actually directly calls ConfigurationManager.AppSettings. So the two can be used interchangeably. Same is true for ConnectionStrings.

    Just wanted to point this out since you seem to have gone way beyond what you needed to do to get the same config working in tests. In actuality you could have probably kept everything the same, and just copied the Web.config to App.config in the test directory and been done with it.

    Or better yet use the settings file and then access the namespace when ever you need to read these settings. This works the same in Web.config and App.config so it should work for both your tests and your webs.

    Reply

Leave a reply

required

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>