C# 如何使用 moq 模拟 ConfigurationManager.AppSettings

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9486087/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 07:36:45  来源:igfitidea点击:

How to mock ConfigurationManager.AppSettings with moq

c#unit-testingmoq

提问by Otuyh

I am stuck at this point of code that I do not know how to mock:

我被困在我不知道如何模拟的代码点上:

ConfigurationManager.AppSettings["User"];

I have to mock the ConfigurationManager, but I don't have a clue, I am using Moq.

我必须模拟 ConfigurationManager,但我不知道,我正在使用Moq

Someone can give me a tip? Thanks!

有人可以给我一个提示吗?谢谢!

采纳答案by Joshua Enfield

I believe one standard approach to this is to use a facadepattern to wrap the configuration manager and then you have something loosely coupled that you have control over.

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

So you would wrap the ConfigurationManager. Something like:

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

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

(You can just extract an interface from your configuration class and then use that interface everywhere in your code) Then you just mock the IConfiguration. You might be able to implement the facade itself in a few different ways. Above I chose just to wrap the individual properties. You also obtain the side benefit of having strongly typed information to work with rather than weakly typed hash arrays.

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

回答by Iridio

Maybe is not what you need to accomplish, but have you considered to use an app.config in your test project? So the ConfigurationManager will get the values that you put in the app.config and you don't need to mock anything. This solution works nice for my needs, because I never need to test a "variable" config file.

也许不是你需要完成的,但你有没有考虑在你的测试项目中使用 app.config ?因此 ConfigurationManager 将获取您放入 app.config 中的值,您无需模拟任何内容。这个解决方案非常适合我的需求,因为我从不需要测试“变量”配置文件。

回答by Erik Dietrich

That is a static property, and Moq is designed to Moq instance methods or classes that can be mocked via inheritance. In other words, Moq is not going to be any help to you here.

这是一个静态属性,Moq 被设计为可以通过继承模拟的 Moq 实例方法或类。换句话说,Moq 在这里对您没有任何帮助。

For mocking statics, I use a tool called Moles, which is free. There are other framework isolation tools, like Typemock that can do this too, though I believe those are paid tools.

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

When it comes to statics and testing, another option is to create the static state yourself, though this can often be problematic (as, I'd imagine it would be in your case).

当涉及到静态和测试时,另一种选择是自己创建静态状态,尽管这通常会有问题(因为,我想在您的情况下会是这样)。

And, finally, if the isolation frameworks are not an option and you're committed to this approach, the facade mentioned by Joshua is a good approach, or any approach in general where you factor client code of this away from the business logic that you're using to test.

最后,如果隔离框架不是一种选择并且您致力于这种方法,那么 Joshua 提到的外观是一种很好的方法,或者任何一般的方法,其中您将客户端代码从您的业务逻辑中分离出来用来测试。

回答by Zorayr

You can use shims to modify AppSettingsto a custom NameValueCollectionobject. Here is an example of how you can achieve this:

您可以使用垫片来修改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.        
    }
}

You can read more about shims and fakes at, Isolating Code Under Test with Microsoft Fakes. Hope this helps.

您可以在使用 Microsoft Fakes 隔离测试中的代码中阅读有关 shims 和 fakes 的更多信息。希望这可以帮助。

回答by LosManos

I am using AspnetMvc4. A moment ago I wrote

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

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

in my test method and it worked perfectly.

在我的测试方法中,它工作得很好。

Explanation: the test method runs in a context with app settings taken from, typically a web.configor myapp.config. ConfigurationsManagercan reach this application-global object and manipulate it.

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

Though: If you have a test runner running tests in parallel this is not a good idea.

虽然:如果您有一个测试运行器并行运行测试,这不是一个好主意。

回答by DanielLarsenNZ

Have you considered stubbing instead of mocking? The AppSettingsproperty is a NameValueCollection:

你有没有考虑过存根而不是嘲笑?该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...
    }
}

The benefits are a simpler implementation and no dependency on System.Configuration until you really need it.

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

回答by t3chb0t

I think writing you own app.config provider is a simple task and is more useful then anything else. Especially you should avoid any fakes like shims etc. because as soon as you use them Edit & Continue no longer works.

我认为编写自己的 app.config 提供程序是一项简单的任务,而且比其他任何事情都更有用。特别是你应该避免任何像垫片等的假货,因为一旦你使用它们,“编辑并继续”就不再起作用了。

The providers I use look like this:

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

By default they get the values from the App.configbut for unit tests I can override all values and use them in each test independently.

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

There's no need for any interfaces or implement it each time over and over again. I have a utilities dll and use this small helper in many projects and unit tests.

不需要任何接口或一遍又一遍地实现它。我有一个实用程序 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; } }
}

回答by Eike

How about just setting what you need? Because, I don't want to mock .NET, do I...?

只设置你需要的怎么样?因为,我不想模拟 .NET,我...?

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

You probably should clean out the AppSettings beforehand to make sure the app only sees what you want it to.

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