[C#] moq로 ConfigurationManager.AppSettings를 모의하는 방법

나는 조롱하는 방법을 모르는이 코드 지점에 갇혀 있습니다.

ConfigurationManager.AppSettings["User"];

ConfigurationManager를 조롱해야하지만 단서가 없습니다 . Moq를 사용하고 있습니다.

누군가 나에게 팁을 줄 수 있습니까? 감사!



답변

이에 대한 한 가지 표준 접근 방식 은 구성 관리자를 래핑 하기 위해 파사드 패턴 을 사용하고 느슨하게 결합되어 제어 할 수있는 것입니다.

따라서 ConfigurationManager를 래핑합니다. 다음과 같은 것 :

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

(구성 클래스에서 인터페이스를 추출한 다음 코드의 모든 곳에서 해당 인터페이스를 사용할 수 있습니다.) 그런 다음 IConfiguration을 모의합니다. 몇 가지 다른 방법으로 파사드 자체를 구현할 수 있습니다. 위에서 저는 개별 속성을 래핑하기로 선택했습니다. 또한 약한 형식의 해시 배열이 아닌 강력한 형식의 정보를 사용하는 부수적 인 이점도 얻을 수 있습니다.


답변

AspnetMvc4를 사용하고 있습니다. 조금 전에 나는 썼다

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

내 테스트 방법에서 완벽하게 작동했습니다.

설명 : 테스트 메소드는 일반적으로 web.config또는 에서 가져온 앱 설정이있는 컨텍스트에서 실행됩니다 myapp.config. ConfigurationsManager이 응용 프로그램 전역 개체에 도달하여 조작 할 수 있습니다.

하지만 : 테스트를 병렬로 실행하는 테스트 러너가있는 경우 이것은 좋은 생각이 아닙니다.


답변

수행해야 할 작업이 아닐 수도 있지만 테스트 프로젝트에서 app.config를 사용하는 것을 고려 했습니까? 따라서 ConfigurationManager는 app.config에 입력 한 값을 가져 오므로 아무것도 모의 할 필요가 없습니다. 이 솔루션은 “변수”구성 파일을 테스트 할 필요가 없기 때문에 내 요구에 적합합니다.


답변

shim을 사용 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.        
    }
}

shim 및 fake에 대한 자세한 내용은 Isolating Code Under Test with Microsoft Fakes에서 확인할 수 있습니다. 도움이 되었기를 바랍니다.


답변

조롱하는 대신 스터 빙을 고려해 보셨습니까? 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에 대한 종속성이 없다는 것입니다.


답변

이것은 정적 속성이며 Moq는 상속을 통해 모의 될 수있는 Moq 인스턴스 메서드 또는 클래스로 설계되었습니다. 다시 말해, Moq는 여기서 당신에게 어떤 도움도되지 않을 것입니다.

모의 통계를 위해 무료 인 Moles 라는 도구를 사용합니다 . 유료 도구라고 생각하지만 Typemock과 같은 다른 프레임 워크 격리 도구도 있습니다.

정적 및 테스트와 관련하여 또 다른 옵션은 정적 상태를 직접 만드는 것입니다.하지만 이것은 종종 문제가 될 수 있습니다 (귀하의 경우에 해당한다고 생각합니다).

그리고 마지막으로 격리 프레임 워크가 선택 사항이 아니고이 접근 방식에 전념한다면 Joshua가 언급 한 파사드가 좋은 접근 방식이거나 일반적으로 클라이언트 코드를 비즈니스 논리에서 제외하는 모든 접근 방식입니다. 테스트에 사용하고 있습니다.


답변

자신의 app.config 공급자를 작성하는 것은 간단한 작업이며 다른 것보다 더 유용하다고 생각합니다. 특히 shim 등과 같은 가짜는 피해야합니다. 사용하자마자 Edit & Continue가 더 이상 작동하지 않기 때문입니다.

내가 사용하는 공급자는 다음과 같습니다.

기본적으로 그들은 값을 얻지 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; } }
}