如何在测试环境中替换配置文件?

时间:2014-01-21 14:36:13

标签: c# unit-testing mocking mstest

我正在使用MSTest来测试应用程序。测试需要某些特定值(通常不存在)出现在应用程序配置文件中。

因此,我需要在测试运行时替换包含值的已知配置文件,以便System.Configuration.ConfigurationManager指向正确的文件。 (即我通过替换我之前制作的另一个来伪造真实的配置文件)

我可以做到这一切,除了在我的测试执行时,System.Configuration.ConfigurationManager已经读取了配置文件,因此忽略了新值。

示例代码:

    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();
    }

(上面的代码创建了一个包含已知测试值的新配置文件,并在其上指向AppDomain.CurrentDomain(“APP_CONFIG_FILE”)。在生产代码中,这种重新路由到另一个配置文件的技术可以完美地工作,如果在开始时完成申请表)

问题是,以下生产线在通过测试运行时无法检索所需的测试值:

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

原因很明显,虽然生产代码行和测试代码现在指向正确的配置文件,但生产配置文件已经加载到内存中,因此测试配置文件实际上被忽略了。

所以问题是:如何强制System.Configuration.ConfigurationManager重新读取配置文件,或者配置文件怎么可能被伪造?或者,如何在测试期间直接修改内存中的配置文件? (AFAIK我不能使用依赖注入和MOQ来模拟它,因为System.Configuration.ConfigurationManager是静态的)

TIA

1 个答案:

答案 0 :(得分:0)

我建议您独立于其他真实类(如ConfigurationManager)进行测试,特别是与环境(文件,网络,数据库等)隔离,因为您的测试可能因某些与之无关的外部原因而失败您正在测试的代码(文件可能不存在,数据库连接错误等)。如果您要创建自己的非静态配置管理器,将所有工作委派给ConfigurationManager,这很容易做到:

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

然后让你的sut(测试中的类)依赖于易于模拟的ICoolConfigurationProvider抽象(考虑返回比名称 - 值集合更具商业性的东西):

public interface IConfigurationProvider
{
     NameValueCollection GetScheduledTasksSettings();
}

sut看起来像:

public class SUT
{
    private IConfigurationProvider _configProvider;

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

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

现在,您可以轻松地为测试提供任何值:

[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
}