如何使用Moq模拟ConfigurationManager.AppSettings


122

我被困在我不知道如何嘲笑的代码上:

ConfigurationManager.AppSettings["User"];

我必须模拟ConfigurationManager,但没有任何线索,我正在使用Moq

有人可以给我小费吗?谢谢!

Answers:


103

我相信一种标准的方法是使用外观模式包装配置管理器,然后您可以控制某些松散耦合的事物。

因此,您将包装ConfigurationManager。就像是:

public class Configuration: IConfiguration
{
    public User
    {
        get
        { 
            return ConfigurationManager.AppSettings["User"];
        }
    }
}

(您可以只从配置类中提取一个接口,然后在代码中的任何地方使用该接口)然后您可以模拟IConfiguration。您可能能够以几种不同的方式实现外观本身。在上面,我选择只包装单个属性。您还获得了使用强类型信息而不是弱类型哈希数组来工作的附带好处。


6
从概念上讲,这也是我在做什么。但是,我使用Castle DictionaryAdapter(Castle Core的一部分)来动态生成接口的实现。我已经写过一段时间了:blog.andreloker.de/post/2008/09/05/…(向下滚动至“解决方案”以了解我如何使用Castle DictionaryAdapter)
Andre Loker

那太漂亮了,那是一篇好文章。我将在未来牢记这一点。
约书亚·恩菲尔德

我还可以添加-根据您的纯粹主义和解释-可以代替或也可以将其称为委托代理或适配器。
约书亚·恩菲尔德

3
从上面看,只是将Moq用作“正常”。未经测试,但类似:var configurationMock = new Mock<IConfiguration>();和设置:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Joshua Enfield

当层依赖于IConfiguration并且您需要模拟IConfiguration时使用此方案,但是如何测试IConfiguration实现?并且,如果您调用单元测试ConfigurationManager.AppSettings [“ User”],则这将不会测试单元,但是会测试从配置文件中获取的值,这不是单元测试。如果您需要检查实现,请参阅@ zpbappi.com/testing-codes-with-configurationmanager-appsettings
nkalfov

172

我正在使用AspnetMvc4。刚才我写了

ConfigurationManager.AppSettings["mykey"] = "myvalue";

在我的测试方法中,效果很好。

说明:测试方法在上下文环境中运行,且应用程序设置通常取自web.configmyapp.configConfigurationsManager可以访问此应用程序全局对象并对其进行操作。

但是:如果您有一个测试运行程序并行运行测试,那么这不是一个好主意。


8
这是解决问题的真正聪明简单的方法!为简单而赞誉!
Navap

1
在大多数情况下,比创建抽象要容易得多
Michael Clark

2
而已????当我为如何测试这个特殊的密封类而绞尽脑汁的时候,它的精巧之处就在于其简单性。
Piotr Kula

5
ConfigurationManager.AppSettingsNameValueCollection不安全的线程,因此在没有适当同步的情况下使用它进行并行测试也不是一个好主意。否则,你可以叫ConfigurationManager.AppSettings.Clear()你的TestInitialize/构造函数,你是金色的。
Ohad Schneider

1
简单明了。迄今为止最好的答案!
znn

21

也许这不是您需要完成的,但是您是否考虑过在测试项目中使用app.config?因此,ConfigurationManager将获取您放入app.config中的值,并且您无需模拟任何内容。该解决方案可以很好地满足我的需求,因为我不需要测试“变量”配置文件。


7
如果被测代码的行为根据配置值的不同而变化,那么如果它不直接依赖于AppSettings,则测试起来无疑会更加容易。
安德烈·洛克

2
这是一个坏习惯,因为您永远不会测试其他可能的设置。Joshua Enfield的答案非常适合测试。
mkaj 2015年

4
虽然其他人反对这个答案,但我想说他们的立场有些笼统。在某些情况下,这是一个非常有效的答案,它实际上仅取决于您的需求。例如,假设我有4个不同的群集,每个群集都有不同的基本URL。在运行时,将这4个集群从Web.config整个项目中拉出。在测试期间,从中提取一些众所周知的值app.config是非常有效的。单元测试只需要确保“ cluster1”起作用的条件就可以起作用。在这种情况下,只有四个不同的集群。
Mike Perrenoud 2015年

14

您可以使用垫片将其修改AppSettings为自定义NameValueCollection对象。这是一个如何实现此目的的示例:

[TestMethod]
public void TestSomething()
{
    using(ShimsContext.Create()) {
        const string key = "key";
        const string value = "value";
        ShimConfigurationManager.AppSettingsGet = () =>
        {
            NameValueCollection nameValueCollection = new NameValueCollection();
            nameValueCollection.Add(key, value);
            return nameValueCollection;
        };

        ///
        // Test code here.
        ///

        // Validation code goes here.        
    }
}

您可以在使用Microsoft Fakes隔离受测代码上了解有关垫片和假货的更多信息。希望这可以帮助。


6
作者要求的是起订量,而不是MS Fakes。
JPCF 2013年

6
这有什么不同?它通过从他的代码中删除数据依赖来实现模拟。使用C#伪造品是一种方法!
Zorayr

9

您是否考虑过存根而不是嘲笑?该AppSettings属性是NameValueCollection

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        // Arrange
        var settings = new NameValueCollection {{"User", "Otuyh"}};
        var classUnderTest = new ClassUnderTest(settings);

        // Act
        classUnderTest.MethodUnderTest();

        // Assert something...
    }
}

public class ClassUnderTest
{
    private readonly NameValueCollection _settings;

    public ClassUnderTest(NameValueCollection settings)
    {
        _settings = settings;
    }

    public void MethodUnderTest()
    {
        // get the User from Settings
        string user = _settings["User"];

        // log
        Trace.TraceInformation("User = \"{0}\"", user);

        // do something else...
    }
}

好处是实现起来更简单,并且在您真正需要它之前不依赖System.Configuration。


3
我最喜欢这种方法。一方面,用IConfiguration约书亚·恩菲尔德(Joshua Enfield)的建议将配置管理器包装起来可能太高了,并且您可能会错过由于错误的配置值解析等问题而存在的错误。另一方面,ConfigurationManager.AppSettings如LosManos所建议的那样直接使用太多的实现细节,更不用说它可能对其他测试产生副作用,并且如果没有手动同步就不能在并行测试运行中使用(因为NameValueConnection这不是线程安全的)。
Ohad Schneider

2

那是一个静态属性,Moq旨在用于Moq实例方法或可通过继承模拟的类。换句话说,起订量不会对您有任何帮助。

对于模拟静态,我使用了一个名为Moles的工具,该工具是免费的。还有其他框架隔离工具,例如Typemock也可以做到这一点,尽管我相信这些工具是付费的。

当涉及到静态和测试时,另一种选择是自己创建静态,尽管这经常会出现问题(例如,我想您会遇到这种情况)。

最后,如果隔离框架不是一种选择,而您仍然致力于这种方法,那么Joshua提到的外观是一个很好的方法,或者是任何通常将客户代码从您的业务逻辑中分解出来的方法正在测试。


1

我认为编写您自己的app.config提供程序是一项简单的任务,比其他任何事情都有用。特别是您应避免使用垫片等伪造品,因为一旦使用它们,“编辑并继续”功能将不再起作用。

我使用的提供程序如下所示:

默认情况下,它们从中获取值,App.config但对于单元测试,我可以覆盖所有值并在每个测试中独立使用它们。

不需要任何接口或一次又一次地实现它。我有一个实用程序dll,并在许多项目和单元测试中使用了此小助手。

public class AppConfigProvider
{
    public AppConfigProvider()
    {
        ConnectionStrings = new ConnectionStringsProvider();
        AppSettings = new AppSettingsProvider();
    }

    public ConnectionStringsProvider ConnectionStrings { get; private set; }

    public AppSettingsProvider AppSettings { get; private set; }
}

public class ConnectionStringsProvider
{
    private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    public string this[string key]
    {
        get
        {
            string customValue;
            if (_customValues.TryGetValue(key, out customValue))
            {
                return customValue;
            }

            var connectionStringSettings = ConfigurationManager.ConnectionStrings[key];
            return connectionStringSettings == null ? null : connectionStringSettings.ConnectionString;
        }
    }

    public Dictionary<string, string> CustomValues { get { return _customValues; } }
}

public class AppSettingsProvider
{
    private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    public string this[string key]
    {
        get
        {
            string customValue;
            return _customValues.TryGetValue(key, out customValue) ? customValue : ConfigurationManager.AppSettings[key];
        }
    }

    public Dictionary<string, string> CustomValues { get { return _customValues; } }
}

1

仅设置所需的内容怎么样?因为,我不想模拟.NET,我...?

System.Configuration.ConfigurationManager.AppSettings["myKey"] = "myVal";

您可能应该事先清除AppSettings,以确保该应用程序只看到您想要的内容。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.