developer tip

정적 메서드 모의

optionbox 2020. 11. 15. 11:13
반응형

정적 메서드 모의


최근에는 단위 테스트에 Moq사용하기 시작했습니다 . Moq를 사용하여 테스트 할 필요가없는 클래스를 모의합니다.

일반적으로 정적 메서드를 어떻게 처리합니까?

public void foo(string filePath)
{
    File f = StaticClass.GetFile(filePath);
}

이 정적 메서드가 StaticClass.GetFile()어떻게 조롱 될 수 있습니까?

추신 : Moq 및 단위 테스트에 대해 추천 해 주신 모든 자료에 감사드립니다.


Moq 또는 Rhinomocks와 같은 모의 프레임 워크는 개체의 모의 인스턴스 만 만들 수 있습니다. 즉, 정적 메서드 모의는 불가능합니다.

Google 에서 자세한 정보를 검색 할 수도 있습니다 .

또한 이전에 StackOverflow에 대한 몇 가지 질문이 여기 , 여기여기에 있습니다 .


@ Pure.Krome : 좋은 답변이지만 몇 가지 세부 사항을 추가하겠습니다

@Kevin : 코드에 가져올 수있는 변경 사항에 따라 솔루션을 선택해야합니다.
변경할 수있는 경우 일부 종속성 주입으로 코드를 더 테스트 할 수 있습니다. 할 수 없다면 좋은 격리가 필요합니다.
무료 모의 프레임 워크 (Moq, RhinoMocks, NMock ...)를 사용하면 델리게이트, 인터페이스 및 가상 메서드 만 모의 할 수 있습니다. 따라서 정적, 봉인 및 비가 상 방법의 경우 3 가지 솔루션이 있습니다.

  • TypeMock Isolator (모든 것을 모의 할 수 있지만 비싸다)
  • Telerik의 JustMock (신규, 저렴하지만 여전히 무료는 아님)
  • Moles of Microsoft (단독을위한 유일한 무료 솔루션)

무료이고 효율적이며 Moq와 같은 람다 식을 사용하기 때문에 Moles를 추천 합니다. 한 가지 중요한 세부 사항 : 두더지는 모의가 아닌 스텁을 제공합니다. 따라서 인터페이스 및 델리게이트에 Moq를 계속 사용할 수 있습니다.)

Mock : 인터페이스를 구현하고 특정 메서드에서 throw 할 반환 / 예외 값을 동적으로 설정하는 기능을 허용하고 특정 메서드가 호출되었는지 여부를 확인하는 기능을 제공하는 클래스입니다.
스텁 : 모의 클래스와 비슷 하지만 메서드가 호출되었는지 / 호출되지 않았는지 확인하는 기능을 제공하지 않는다는 점이 다릅니다.


.NET에서는 MOQ 및 기타 모의 라이브러리를 제외한 가능성이 있습니다. 모의하려는 정적 메서드가 포함 된 어셈블리에서 솔루션 탐색기를 마우스 오른쪽 단추로 클릭하고 Add Fakes Assembly를 선택해야합니다 . 다음으로 어셈블리 정적 메서드를 자유롭게 모의 할 수 있습니다.

System.DateTime.Now정적 메서드 를 모의한다고 가정합니다 . 예를 들어 다음과 같이하십시오.

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => new DateTime(1837, 1, 1);
    Assert.AreEqual(DateTime.Now.Year, 1837);
}

각 정적 속성 및 메서드에 대해 유사한 속성이 있습니다.


nuget에서 제공 하는 Pose 라이브러리를 사용 하여이를 달성 할 수 있습니다 . 무엇보다도 정적 메서드를 모의 할 수 있습니다. 테스트 방법에서 다음을 작성하십시오.

Shim shim = Shim.Replace(() => StaticClass.GetFile(Is.A<string>()))
    .With((string name) => /*Here return your mocked value for test*/);
var sut = new Service();
PoseContext.Isolate(() =>
    result = sut.foo("filename") /*Here the foo will take your mocked implementation of GetFile*/, shim);

자세한 내용은 여기를 참조하십시오 https://medium.com/@tonerdo/unit-testing-datetime-now-in-c-without-using-interfaces-978d372478e8


나는 Pose를 좋아했지만 알려진 문제로 보이는 InvalidProgramException 던지기를 멈출 수 없었습니다 . 이제 다음 과 같이 Smocks를 사용 하고 있습니다.

Smock.Run(context =>
{
    context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));

    // Outputs "2000"
    Console.WriteLine(DateTime.Now.Year);
});

테스트 목적으로 외부에서 설정할 수있는 대리자를 호출하기 위해 정적 메서드를 리팩토링하는 개념을 가지고 놀았습니다.

이것은 테스트 프레임 워크를 사용하지 않으며 완전히 맞춤화 된 솔루션이지만 리팩터링은 호출자의 서명에 영향을주지 않으므로 상대적으로 안전합니다.

이 작업을 수행하려면 정적 메서드에 대한 액세스 권한이 있어야하므로 System.DateTime.

다음은 두 개의 매개 변수를 사용하는 반환 유형과 반환 유형이없는 제네릭 하나 인 두 가지 정적 메서드를 만든 곳에서 놀아 온 예제입니다.

주요 정적 클래스 :

public static class LegacyStaticClass
{
    // A static constructor sets up all the delegates so production keeps working as usual
    static LegacyStaticClass()
    {
        ResetDelegates();
    }

    public static void ResetDelegates()
    {
        // All the logic that used to be in the body of the static method goes into the delegates instead.
        ThrowMeDelegate = input => throw input;
        SumDelegate = (a, b) => a + b;
    }

    public static Action<Exception> ThrowMeDelegate;
    public static Func<int, int, int> SumDelegate;

    public static void ThrowMe<TException>() where TException : Exception, new()
        => ThrowMeDelegate(new TException());

    public static int Sum(int a, int b)
        => SumDelegate(a, b);
}

단위 테스트 (xUnit 및 Shouldly)

public class Class1Tests : IDisposable
{
    [Fact]
    public void ThrowMe_NoMocking_Throws()
    {
        Should.Throw<Exception>(() => LegacyStaticClass.ThrowMe<Exception>());
    }

    [Fact]
    public void ThrowMe_EmptyMocking_DoesNotThrow()
    {
        LegacyStaticClass.ThrowMeDelegate = input => { };

        LegacyStaticClass.ThrowMe<Exception>();

        true.ShouldBeTrue();
    }

    [Fact]
    public void Sum_NoMocking_AddsValues()
    {
        LegacyStaticClass.Sum(5, 6).ShouldBe(11);
    }

    [Fact]
    public void Sum_MockingReturnValue_ReturnsMockedValue()
    {
        LegacyStaticClass.SumDelegate = (a, b) => 6;
        LegacyStaticClass.Sum(5, 6).ShouldBe(6);
    }

    public void Dispose()
    {
        LegacyStaticClass.ResetDelegates();
    }
}

나는 이것이 조금 늦었다는 것을 알고 있지만이 원형 교차로 솔루션을 사용하면 Moq를 사용하여 정적 메서드를 모의 할 수 있습니다.

이를 위해 저는 Placeholderstatic method라는 하나의 메서드를 가진 클래스를 만들었습니다 StaticClass.GetFile.

public class Placeholder{  

    //some empty constructor

    public File GetFile(){

        File f = StaticClass.GetFile(filePath);
        return f;
    }
}

그런 다음, 호출하는 대신 StaticClass.GetFilefoo, 나는의 인스턴스를 생성 Placeholder및 호출 GetFile기능.

public void foo(string filePath)
{
    Placeholder p = new Placeholder();
    File f = p.GetFile(filePath);
}

이제 단위 테스트에서 mock을 시도하는 대신 클래스 에서 StaticClass.GetFile비 정적 GetFile메서드 를 모의 할 수있었습니다 Placeholder.

참고 URL : https://stackoverflow.com/questions/5864076/mocking-static-methods

반응형