Microsoft Fakes를 사용하여 테스트 중인 코드 격리

적용 대상:yesVisual Studio noMac용 Visual Studio noVisual Studio Code

Microsoft Fakes는 스텁 또는 shim을 사용하는 애플리케이션의 다른 부분을 교체함으로써 사용자가 테스트 중인 코드를 격리시켜 줍니다. 스텁 및 shim은 테스트에서 제어하는 작은 코드 조각입니다. 테스트를 위해 코드를 격리하여 테스트가 실패할 경우 원인이 어디에 있는지 파악합니다. 애플리케이션의 다른 부분이 아직 작동하지 않더라도 스텁 및 shim을 사용해서 코드를 테스트할 수도 있습니다.

Fakes는 두 가지 버전이 있습니다.

  • 스텁은 클래스를 동일한 인터페이스를 구현하는 작은 대안으로 바뀝니다. 스텁을 사용하려면 각 구성 요소가 다른 구성 요소에 종속되는 것이 아니라 인터페이스에만 종속되도록 애플리케이션을 설계해야 합니다. ("구성 요소"란 함께 설계되어 업데이트되고 대개 하나의 어셈블리에 포함되는 클래스 또는 클래스의 그룹을 의미합니다.)

  • shim은 지정된 메서드 콜을 실행하는 대신 테스트에서 제공하는 shim 코드를 실행할 수 있도록 런타임에 애플리케이션의 컴파일된 코드를 수정합니다. shim은 .NET 어셈블리와 같이 수정할 수 없는 어셈블리에 대한 호출을 바꾸는 데 사용할 수 있습니다.

    Fakes replace other components

요구 사항

  • Visual Studio Enterprise
  • .NET Framework 프로젝트
  • Visual Studio 2019 업데이트 6에서 미리 보기로 제공한 .NET Core, .NET 5.0 이상 및 SDK 스타일 프로젝트 지원은 업데이트 8에서 기본적으로 사용하도록 설정되어 있습니다. 자세한 내용은 .NET Core 및 SDK 스타일 프로젝트용 Microsoft Fakes를 참조하세요.

참고

Visual Studio를 이용한 프로파일링은 Microsoft Fakes를 사용하는 테스트에는 사용할 수 없습니다.

스텁 및 shim 형식 중에 선택

일반적으로 이러한 클래스를 동시에 개발하고 업데이트했으므로 Visual Studio 프로젝트를 구성 요소라고 생각할 수 있습니다. 프로젝트가 솔루션의 다른 프로젝트에 대해 만드는 호출 또는 프로젝트가 참조하는 다른 어셈블리에 대해 만드는 호출에 스텁 또는 shim을 사용하는 것을 고려할 수 있습니다.

Visual Studio 솔루션에 포함된 호출에는 스텁을 사용하고 그 밖의 참조된 어셈블리 호출에는 shim을 사용할 수 있습니다. 고유 솔루션 내에서는 스텁에 필요한 방식으로 인터페이스를 정의하여 구성 요소를 분리하는 것이 좋기 때문입니다. 그러나 System.dll 등의 외부 어셈블리에는 별도의 인터페이스 정의가 제공되지 않는 것이 일반적이므로 대신 shim을 사용해야 합니다.

기타 고려 사항:

성능. shim은 런타임에 코드를 다시 작성하기 때문에 느리게 실행됩니다. 스텁은 이러한 성능 오버헤드가 없고 가상 메서드처럼 빠릅니다.

Sealed 형식의 정적 메서드. 인터페이스를 구현할 때는 스텁만 사용할 수 있습니다. 따라서 스텁 형식은 정적 메서드, 비가상 메서드, 봉인된 가상 메서드, 봉인된 형식의 메서드 등에 사용할 수 없습니다.

내부 형식. 스텁과 shim 둘 다 어셈블리 특성 InternalsVisibleToAttribute를 사용하여 액세스할 수 있는 내부 형식에 사용할 수 있습니다.

전용 메서드. 메서드 시그니처의 모든 형식이 표시되는 경우 shim은 개인 메서드에 대한 호출을 대체할 수 있습니다. 스텁은 표시되는 메서드만 바꿀 수 있습니다.

인터페이스 및 추상 메서드. 스텁은 테스트에서 사용할 수 있는 추상 메서드의 구현 및 인터페이스를 제공합니다. shim에는 메서드 본문이 없기 때문에 인터페이스 및 추상 메서드를 계측할 수 없습니다.

코드베이스 안의 종속성에서 격리하려면 스텁 형식을 사용하는 것이 좋습니다. 인터페이스 뒤에 구성 요소를 숨기면 됩니다. shim 형식을 사용하여 테스트 가능한 API를 제공하지 않는 타사 구성 요소에서 격리할 수 있습니다.

스텁 시작

자세한 내용은 스텁을 사용하여 유닛 테스트를 위한 애플리케이션의 여러 부분을 서로 격리를 참조하세요.

  1. 인터페이스 삽입

    스텁을 사용하려면 애플리케이션의 다른 구성 요소에서 클래스를 명시적으로 지정하지 않는 방식으로 테스트할 코드를 작성해야 합니다. "구성 요소"란 함께 개발되어 업데이트되며 일반적으로 하나의 Visual Studio 프로젝트에 포함되는 클래스를 의미합니다. 변수 및 매개 변수는 인터페이스를 사용하여 선언해야 하며 다른 구성 요소의 인스턴스는 팩터리를 사용하여 전달하거나 만들어야 합니다. 예를 들어, StockFeed가 애플리케이션의 다른 구성 요소에 있는 클래스인 경우 잘못된 것으로 간주됩니다.

    return (new StockFeed()).GetSharePrice("COOO"); // Bad

    대신, 다른 구성 요소가 구현하고 스텁이 테스트 용도로 실행할 수도 있는 인터페이스를 정의할 수 있습니다.

    public int GetContosoPrice(IStockFeed feed) => feed.GetSharePrice("COOO");
    
  2. Fakes 어셈블리 추가

    1. 솔루션 탐색기에서

      • 이전 .NET Framework 프로젝트(비 SDK 스타일)의 경우 단위 테스트 프로젝트의 참조 노드를 확장합니다.

      • .NET Framework, .NET Core 또는 .NET 5.0 이상을 대상으로 하는 SDK 스타일 프로젝트의 경우 종속성 노드를 확장하여 어셈블리, 프로젝트 또는 패키지에서 모조할 어셈블리를 찾습니다.

      • Visual Basic에서 작업하는 경우 솔루션 탐색기 도구 모음에서 모든 파일 표시를 선택하여 참조 노드를 봅니다.

    2. shim을 만들 클래스 정의가 포함된 어셈블리를 선택합니다. 예를 들어 날짜/시간을 shim하려면 System.dll을 선택합니다.

    3. 바로 가기 메뉴에서 Fakes 어셈블리 추가를 선택합니다.

  3. 테스트에서 스텁 인스턴스를 생성하고 해당 메서드에 대한 코드를 제공합니다.

    [TestClass]
    class TestStockAnalyzer
    {
        [TestMethod]
        public void TestContosoStockPrice()
        {
          // Arrange:
    
            // Create the fake stockFeed:
            IStockFeed stockFeed =
                 new StockAnalysis.Fakes.StubIStockFeed() // Generated by Fakes.
                     {
                         // Define each method:
                         // Name is original name + parameter types:
                         GetSharePriceString = (company) => { return 1234; }
                     };
    
            // In the completed application, stockFeed would be a real one:
            var componentUnderTest = new StockAnalyzer(stockFeed);
    
          // Act:
            int actualValue = componentUnderTest.GetContosoPrice();
    
          // Assert:
            Assert.AreEqual(1234, actualValue);
        }
        ...
    }
    

    여기에서 특별한 부분은 StubIStockFeed 클래스입니다. Microsoft Fakes는 참조된 어셈블리의 모든 인터페이스에 대해 스텁 클래스를 생성합니다. 스텁 클래스의 이름은 인터페이스의 이름에서 파생되며 “Fakes.Stub”이 접두사가 되고 매개 변수 형식 이름이 추가됩니다.

    스텁은 속성, 이벤트 및 제네릭 메서드의 getter와 setter에 대해서도 생성됩니다. 자세한 내용은 스텁을 사용하여 유닛 테스트를 위한 애플리케이션의 여러 부분을 서로 격리를 참조하세요.

shim 시작

(자세한 내용은 shim을 사용하여 유닛 테스트를 위한 다른 어셈블리에서 애플리케이션 격리를 참조하세요.)

구성 요소에 DateTime.Now에 대한 호출이 포함된 경우를 가정합니다.

// Code under test:
    public int GetTheCurrentYear()
    {
       return DateTime.Now.Year;
    }

실제 버전은 호출할 때마다 매번 다른 값을 반환하므로 테스트하는 동안에는 Now 속성을 shim하려고 합니다.

shim을 사용하기 위해 애플리케이션 코드를 수정하거나 특정 방식으로 쓰지 않아도 됩니다.

  1. Fakes 어셈블리 추가

    솔루션 탐색기에서 단위 테스트 프로젝트의 참조를 열고 모조하려는 메서드가 포함된 어셈블리에 대한 참조를 선택합니다. 이 예제에서 DateTime 클래스는 System.dll에 있습니다. Visual Basic 프로젝트에서 참조를 보려면 모든 파일 표시를 선택합니다.

    Fakes 어셈블리 추가를 선택합니다.

  2. ShimsContext에 shim 삽입

    [TestClass]
    public class TestClass1
    {
            [TestMethod]
            public void TestCurrentYear()
            {
                int fixedYear = 2000;
    
                // Shims can be used only in a ShimsContext:
                using (ShimsContext.Create())
                {
                  // Arrange:
                    // Shim DateTime.Now to return a fixed date:
                    System.Fakes.ShimDateTime.NowGet =
                    () =>
                    { return new DateTime(fixedYear, 1, 1); };
    
                    // Instantiate the component under test:
                    var componentUnderTest = new MyComponent();
    
                  // Act:
                    int year = componentUnderTest.GetTheCurrentYear();
    
                  // Assert:
                    // This will always be true if the component is working:
                    Assert.AreEqual(fixedYear, year);
                }
            }
    }
    

    shim 클래스 이름은 원래 형식 이름에 Fakes.Shim 접두사를 추가하여 구성합니다. 매개 변수 이름이 메서드 이름에 추가됩니다. (System.Fakes에는 어셈블리 참조를 추가할 필요가 없습니다.)

이전 예제에서는 정적 메서드에 대해 shim을 사용합니다. 인스턴스 메서드에 shim을 사용하려면 형식 이름과 메서드 이름 사이에 AllInstances를 씁니다.

System.IO.Fakes.ShimFile.AllInstances.ReadToEnd = ...

(참조할 System.IO.Fakes 어셈블리가 없습니다. 네임스페이스는 shim 만들기 프로세스에 의해 생성됩니다. 그러나 일반적인 방법으로 'using' 또는 'Import'를 사용할 수 있습니다.)

또한 특정 인스턴스, 생성자 및 속성에 대한 shim을 만들 수 있습니다. 자세한 내용은 shim을 사용하여 유닛 테스트를 위한 다른 어셈블리에서 애플리케이션 격리를 참조하세요.

CI에서 Microsoft Fakes 사용

Microsoft Fakes 어셈블리 생성

Microsoft Fakes에는 Visual Studio Enterprise가 필요하므로, Fakes 어셈블리를 생성하려면 Visual Studio Build 작업을 사용하여 프로젝트를 빌드해야 합니다.

참고

이에 대한 대안은 Fakes 어셈블리를 CI에 체크 인하고 MSBuild 작업을 사용하는 것입니다. 이 작업을 수행하는 경우 다음 코드 조각과 같이 테스트 프로젝트에 생성된 Fakes 어셈블리에 대한 어셈블리 참조가 있는지 확인해야 합니다.

<Project Sdk="Microsoft.NET.Sdk">
    <ItemGroup>
        <Reference Include="FakesAssemblies\System.Fakes.dll"/>
    </ItemGroup>
</Project>

어셈블리 참조를 암시적으로 테스트 프로젝트에 추가하도록 변경했기 때문에 이 참조는 특히 SDK 스타일 프로젝트(.NET Core, .NET 5.0 및 .NET Framework)에 수동으로 추가해야 합니다. 이 방법을 따르는 경우 부모 어셈블리가 변경될 때 Fakes 어셈블리가 업데이트되도록 해야 합니다.

Microsoft Fakes 테스트 실행

Microsoft Fakes 어셈블리가 구성된 FakesAssemblies 디렉터리(기본값 $(ProjectDir)FakesAssemblies)에 있는 한, vstest 작업을 사용하여 테스트를 실행할 수 있습니다.

Microsoft Fakes를 사용하는 .NET Core 및 .NET 5.0 이상 프로젝트에서 vstest 작업으로 분산 테스트를 실행하려면 Visual Studio 2019 업데이트 9 미리 보기 20201020-06 이상이 필요합니다.

Microsoft Fakes를 사용하는 .NET Framework 테스트 프로젝트를 SDK 스타일 .NET Framework, .NET Core 또는 .NET 5.0 이상 프로젝트로 전환

Microsoft Fakes에 대해 설정된 .NET Framework를 .NET Core 또는 .NET 5.0으로 전환하려면 최소한의 변경만 필요합니다. 고려해야 할 사례는 다음과 같습니다.

  • 사용자 지정 프로젝트 템플릿을 사용하는 경우 해당 템플릿이 SDK 스타일이고 호환되는 대상 프레임워크에 대한 빌드인지 확인해야 합니다.

  • 일부 형식은 .NET Framework 및 .NET Core/.NET 5.0에서 서로 다른 어셈블리에 있습니다. 예를 들어 System.DateTime은 .NET Framework에서는 System/mscorlib에 있고 .NET Core 및 .NET 5.0에서는 System.Runtime에 있습니다. 이러한 시나리오에서는 모조되는 어셈블리를 변경해야 합니다.

  • Fakes 어셈블리 및 테스트 프로젝트에 대한 어셈블리 참조가 있는 경우 다음과 같은 누락된 참조에 대한 빌드 경고가 표시될 수 있습니다.

    (ResolveAssemblyReferences target) ->
    warning MSB3245: Could not resolve this reference. Could not locate the assembly "AssemblyName.Fakes". Check to make sure the assembly exists on disk.
    If this reference is required by your code, you may get compilation errors.
    

    이 경고는 Fakes 생성에서 수행해야 하는 변경 때문에 발생하며 무시할 수 있습니다. 이제 빌드 중에 어셈블리 참조를 암시적으로 추가하기 때문에 프로젝트 파일에서 어셈블리 참조를 제거하여 이 경고를 방지할 수 있습니다.

Microsoft Fakes 지원

.NET Framework를 대상으로 하는 이전 프로젝트의 Microsoft Fakes(비 SDK 스타일).

  • Microsoft Fakes 어셈블리 생성은 Visual Studio Enterprise 2015 이상에서 지원됩니다.
  • Microsoft Fakes 테스트는 모든 사용 가능한 Microsoft TestPlatform NuGet 패키지를 사용하여 실행할 수 있습니다.
  • Visual Studio Enterprise 2015 이상에서 Microsoft Fakes를 사용하는 테스트 프로젝트에 대해 코드 검사가 지원됩니다.

SDK 스타일 .NET Framework, .NET Core, .NET 5.0 이상 프로젝트의 Microsoft Fakes

  • Microsoft Fakes 어셈블리 생성은 Visual Studio Enterprise 2019 업데이트 6에서는 미리 보기로 지원되고 업데이트 8에서는 기본적으로 사용하도록 설정되어 있습니다.
  • .NET Framework를 대상으로 하는 프로젝트에 대한 Microsoft Fakes 테스트는 사용 가능한 모든 Microsoft TestPlatform NuGet 패키지를 사용하여 실행할 수 있습니다.
  • .NET Core 및 .NET 5.0 이상을 대상으로 하는 프로젝트에 대한 Microsoft Fakes 테스트는 버전이 16.9.0-preview-20210106-01 이상인 Microsoft.TestPlatform NuGet 패키지를 사용하여 실행할 수 있습니다.
  • Visual Studio Enterprise version 2015 이상에서 .NET Framework를 대상으로 Microsoft Fakes를 사용하는 테스트 프로젝트에 대해 코드 검사가 지원됩니다.
  • .NET Core 및 .NET 5.0 이상을 대상으로 Microsoft Fakes를 사용하는 테스트 프로젝트에 대한 코드 검사 지원은 Visual Studio 2019 업데이트 9 이상에서 사용할 수 있습니다.

단원 내용

스텁을 사용하여 단위 테스트를 위한 애플리케이션의 여러 부분을 서로 격리

shim을 사용하여 단위 테스트를 위한 다른 어셈블리에서 애플리케이션 격리

Microsoft Fakes의 코드 생성, 컴파일 및 명명 규칙