2

I am using MSTest to test an application. The test requires certain specific values, which are not normally present, to appear in the application config file.

So I need to substitute a well-known config file containing the values, at test run time, so that System.Configuration.ConfigurationManager points at the right file. (ie I am faking the real config file by substituting another one that I made earlier)

I can do all that, except that by the time my test executes, System.Configuration.ConfigurationManager has already read the config file, so that the new values are ignored.

Example code:

    static TemporaryConfigFile config;
    [ClassInitialize]
    public static void ClassInitialise(TestContext testContext)
    {
        string sourceResource = "Intra_Matrix_Scheduler_Tests.Resources.test.config";
        string tempConfigFileName = "test.config";
        config = TemporaryConfigFile.CreateFromEmbeddedResource(Assembly.GetExecutingAssembly(), sourceResource, tempConfigFileName);
    }

    [ClassCleanup]
    public static void ClassCleanUp()
    {
        config.Dispose();
    }

(the above code creates a new config file with known test values, and points AppDomain.CurrentDomain("APP_CONFIG_FILE") at it. In the production code this technique of rerouting to another config file works perfectly if done at the start of the application)

The problem is that the following production line, when exercised by a test, does not retrieve the desired test values:

        var dict = (System.Collections.Specialized.NameValueCollection)System.Configuration.ConfigurationManager.GetSection("ScheduledTasks");

and the reason is clearly that although the production code line and test code are by now pointing at the correct config file, the production config file has already been loaded into memory so the test config file is effectively ignored.

So the question is: how can System.Configuration.ConfigurationManager be forced to re-read the config file, or how else can the config file be faked? Alternatively how can I directly modify the in-memory config file for the duration of the test? (AFAIK I can't use dependency injection and MOQ to mock it, because System.Configuration.ConfigurationManager is static)

TIA

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
haughtonomous
  • 4,602
  • 11
  • 34
  • 52

1 Answers1

0

I suggest you to test classes in isolation from other real classes (like ConfigurationManager) and especially in isolation from environment (files, network, databases etc), because your tests could fail for some external reason not related to code you are testing (file may not exist, wrong database connection, etc). It's easy to do if you'll create your own non-static configuration manager, which will delegate all work to ConfigurationManager:

public class ConfigurationManagerWrapper : IConfigurationProvider
{
    public NameValueCollection GetScheduledTasksSettings()
    {
        return (NameValueCollection)ConfigurationManager
                  .GetSection("ScheduledTasks");
    }
}

Then make your sut (class under test) depend on ICoolConfigurationProvider abstraction which is easy to mock (consider also to return something more business specific than name-value collection):

public interface IConfigurationProvider
{
     NameValueCollection GetScheduledTasksSettings();
}

And sut looks like:

public class SUT
{
    private IConfigurationProvider _configProvider;

    public SUT(IConfigurationProvider configProvider)
    {
        _configProvider = configProvider;
    }

    public void Exercise()
    {
        var dict = _configProvider.GetScheduledTasksSettings();
        // ...
    }
}

Now you can easily provide any values for your tests:

[TestMethod]
public void ShouldDoSomething()
{       
   var configMock = new Mock<IConfigurationProvider>();

   configMock.Setup(c => c.GetScheduledTasksSettings())
             .Returns(new NameValueCollection {{ "foo", "bar" }});

   var sut = new SUT(configMock.Object); // inject configuration provider
   sut.Exercise();

   // Assertions
}
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • 1
    Thanks, I had stumbled upon something along the same lines before I returned to this thread. Good to know I am on the right track. – haughtonomous Jan 21 '14 at 14:51
  • @NeilHaughton welcome :) unit tests also should be fast - that's another reason to use mocks instead of reading files or making database queries – Sergey Berezovskiy Jan 21 '14 at 14:52
  • I use mocks wherever I can, but sometimes the legacy code just gets in the way and makes it too hard for comfort! – haughtonomous Jan 21 '14 at 14:58