shim을 사용하여 단위 테스트를 위한 다른 어셈블리에서 응용 프로그램 격리Use shims to isolate your application from other assemblies for unit testing

shim 형식은 Microsoft Fakes 프레임워크가 환경에서 테스트 대상 구성 요소를 쉽게 격리시킬 수 있도록 하기 위해 사용하는 두 기술 중 하나입니다.Shim types are one of two technologies that the Microsoft Fakes Framework uses to let you easily isolate components under test from the environment. shim은 특정 메서드 호출을 테스트의 일부로 작성하는 코드로 우회합니다.Shims divert calls to specific methods to code that you write as part of your test. 대부분의 메서드는 외부 조건에 따라 다른 결과를 반환하지만 shim은 테스트에 의해 제어되며 모든 호출에서 일관된 결과를 반환할 수 있습니다.Many methods return different results dependent on external conditions, but a shim is under the control of your test and can return consistent results at every call. 이렇게 하면 테스트를 훨씬 쉽게 작성할 수 있습니다.This makes your tests much easier to write.

shim을 사용하여 솔루션의 일부가 아닌 코드를 어셈블리에서 격리할 수 있습니다.Use shims to isolate your code from assemblies that are not part of your solution. 솔루션의 구성 요소를 서로 격리하려면 스텁을 사용하는 것이 좋습니다.To isolate components of your solution from each other, we recommend that you use stubs.

개요 및 빠른 시작 가이드를 보려면 Microsoft Fakes를 사용하여 테스트 대상 코드 격리를 참조하세요.For an overview and quick start guidance, see Isolating Code Under Test with Microsoft Fakes

요구 사항Requirements

  • Visual Studio EnterpriseVisual Studio Enterprise

예: Y2K 버그Example: The Y2K bug

2000년 1월 1일에 예외를 throw하는 메서드를 살펴보겠습니다.Let's consider a method that throws an exception on January 1st of 2000:

// code under test
public static class Y2KChecker {
    public static void Check() {
        if (DateTime.Now == new DateTime(2000, 1, 1))
            throw new ApplicationException("y2kbug!");
    }
}

이 메서드를 테스트하는 경우 프로그램이 환경에 종속된 비결정적인 메서드인 컴퓨터 시계에 종속된 메서드 DateTime.Now에 종속되기 때문에 문제가 발생합니다.Testing this method is problematic because the program depends on DateTime.Now, a method that depends on the computer's clock, an environment-dependent, non-deterministic method. 또한 DateTime.Now는 정적 속성이므로 여기서 스텁 형식을 사용할 수 없습니다.Furthermore, the DateTime.Now is a static property so a stub type can't be used here. 이 문제는 단위 테스트의 격리 문제를 나타냅니다. 직접 데이터베이스 API를 호출하고, 웹 서비스와 통신하는 등의 프로그램은 해당 논리가 환경에 종속되기 때문에 단위 테스트를 수행하기 어렵습니다.This problem is symptomatic of the isolation issue in unit testing: programs that directly call into database APIs, communicate with web services, and so on, are hard to unit test because their logic depends on the environment.

이런 경우 shim 형식을 사용해야 합니다.This is where shim types should be used. shim 형식은 .NET 메서드를 사용자 정의 대리자로 우회하는 메커니즘을 제공합니다.Shim types provide a mechanism to detour any .NET method to a user-defined delegate. shim 형식은 Fakes 생성기에서 코드로 생성되며 shim 형식이라는 대리자를 사용하여 새 메서드 구현을 지정합니다.Shim types are code-generated by the Fakes generator, and they use delegates, which we call shim types, to specify the new method implementations.

다음 테스트에서는 shim 형식 ShimDateTime을 사용하여 DateTime.Now의 사용자 지정 구현을 제공하는 방법을 보여 줍니다.The following test shows how to use the shim type, ShimDateTime, to provide a custom implementation of DateTime.Now:

//unit test code
// create a ShimsContext cleans up shims
using (ShimsContext.Create()
    // hook delegate to the shim method to redirect DateTime.Now
    // to return January 1st of 2000
    ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);
    Y2KChecker.Check();
}

shim 사용 방법How to use Shims

Fakes 어셈블리 추가Add Fakes Assemblies

  1. 솔루션 탐색기에서 단위 테스트 프로젝트의 참조를 확장합니다.In Solution Explorer, expand your unit test project's References.

    • Visual Basic에서 작업하는 경우 참조 목록을 보려면 솔루션 탐색기 도구 모음에서 모든 파일 표시를 선택해야 합니다.If you are working in Visual Basic, you must select Show All Files in the Solution Explorer toolbar, in order to see the References list.
  2. shim을 만들 클래스 정의가 포함된 어셈블리를 선택합니다.Select the assembly that contains the classes definitions for which you want to create shims. 예를 들어 DateTime을 shim하려면 System.dll을 선택합니다.For example, if you want to shim DateTime, select System.dll

  3. 바로 가기 메뉴에서 Fakes 어셈블리 추가를 선택합니다.On the shortcut menu, choose Add Fakes Assembly.

ShimsContext 사용Use ShimsContext

단위 테스트 프레임워크에서 shim 형식을 사용하는 경우 테스트 코드를 ShimsContext에 래핑하여 shim의 수명을 제어해야 합니다.When using shim types in a unit test framework, you must wrap the test code in a ShimsContext to control the lifetime of your shims. 이렇게 하지 않으면 AppDomain이 종료될 때까지 shim이 지속됩니다.If we didn't require this, your shims would last until the AppDomain shut down. ShimsContext를 만드는 가장 쉬운 방법은 다음 코드와 같이 정적 Create() 메서드를 사용하는 것입니다.The easiest way to create a ShimsContext is by using the static Create() method as shown in the following code:

//unit test code
[Test]
public void Y2kCheckerTest() {
  using(ShimsContext.Create()) {
    ...
  } // clear all shims
}

각 shim 컨텍스트를 올바르게 삭제하는 것이 중요합니다.It is critical to properly dispose each shim context. 경험상, 항상 using 문 내에서 ShimsContext.Create를 호출하여 등록된 shim이 제대로 지워지도록 합니다.As a rule of thumb, always call the ShimsContext.Create inside of a using statement to ensure proper clearing of the registered shims. 예를 들어 항상 2000년 1월 1일을 반환하는 대리자로 DateTime.Now 메서드를 대체하는 테스트 메서드에 대해 shim을 등록할 수 있습니다.For example, you might register a shim for a test method that replaces the DateTime.Now method with a delegate that always returns the first of January 2000. 테스트 메서드에서 등록된 shim을 지우지 않으면 테스트 실행의 나머지 부분에서 항상 2000년 1월 1일을 DateTime.Now 값으로 반환합니다.If you forget to clear the registered shim in the test method, the rest of the test run would always return the first of January 2000 as the DateTime.Now value. 이 결과는 놀라움과 혼동을 줄 수 있습니다.This might be surprising and confusing.

shim을 사용하여 테스트 작성Write a test with shims

테스트 코드에서 모조할 메서드에 대해 우회를 삽입합니다.In your test code, insert a detour for the method you want to fake. 예:For example:

[TestClass]
public class TestClass1
{
        [TestMethod]
        public void TestCurrentYear()
        {
            int fixedYear = 2000;

            using (ShimsContext.Create())
            {
              // Arrange:
                // Detour 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);
            }
        }
}
<TestClass()> _
Public Class TestClass1
    <TestMethod()> _
    Public Sub TestCurrentYear()
        Using s = Microsoft.QualityTools.Testing.Fakes.ShimsContext.Create()
            Dim fixedYear As Integer = 2000
            ' Arrange:
            ' Detour DateTime.Now to return a fixed date:
            System.Fakes.ShimDateTime.NowGet = _
                Function() As DateTime
                    Return New DateTime(fixedYear, 1, 1)
                End Function

            ' Instantiate the component under test:
            Dim componentUnderTest = New MyComponent()
            ' Act:
            Dim year As Integer = componentUnderTest.GetTheCurrentYear
            ' Assert:
            ' This will always be true if the component is working:
            Assert.AreEqual(fixedYear, year)
        End Using
    End Sub
End Class

shim 클래스 이름은 원래 형식 이름에 Fakes.Shim 접두사를 추가하여 구성합니다.Shim class names are made up by prefixing Fakes.Shim to the original type name.

shim은 테스트 대상 응용 프로그램의 코드에 우회를 삽입하여 작동합니다.Shims work by inserting detours into the code of the application under test. 원래 메서드가 호출될 때마다 Fakes 시스템은 실제 메서드를 호출하는 대신 shim 코드가 호출되도록 우회를 수행합니다.Wherever a call to the original method occurs, the Fakes system performs a detour, so that instead of calling the real method, your shim code is called.

우회는 런타임에 생성 및 삭제됩니다.Notice that detours are created and deleted at run time. 항상 ShimsContext의 수명 내에서 우회를 만들어야 합니다.You must always create a detour within the life of a ShimsContext. 삭제하면 활성화된 동안 만든 shim이 모두 제거됩니다.When it is disposed, any shims you created while it was active are removed. 이 작업은 using 문 내에서 수행하는 것이 가장 좋습니다.The best way to do this is inside a using statement.

Fakes 네임스페이스가 없다는 빌드 오류가 표시될 수도 있습니다.You might see a build error stating that the Fakes namespace does not exist. 다른 컴파일 오류가 있을 때 이 오류가 나타나는 경우도 있습니다.This error sometimes appears when there are other compilation errors. 다른 오류를 수정하면 오류가 사라집니다.Fix the other errors and it will vanish.

다양한 메서드에 대한 shimShims for different kinds of methods

shim 형식을 사용하여 정적 메서드 또는 비가상 메서드를 포함하는 .NET 메서드를 사용자 고유의 대리자로 대체할 수 있습니다.Shim types allow you to replace any .NET method, including static methods or non-virtual methods, with your own delegates.

정적 메서드Static methods

정적 메서드에 shim을 연결하는 속성은 shim 형식에 배치됩니다.The properties to attach shims to static methods are placed in a shim type. 각 속성에는 대상 메서드에 대리자를 연결하는 데 사용할 수 있는 setter만 있습니다.Each property has only a setter that can be used to attach a delegate to the target method. 예를 들어 정적 메서드 MyMethod를 포함하는 MyClass 클래스가 있다고 가정합니다.For example, given a class MyClass with a static method MyMethod:

//code under test
public static class MyClass {
    public static int MyMethod() {
        ...
    }
}

항상 5를 반환하는 shim을 MyMethod에 연결할 수 있습니다.We can attach a shim to MyMethod that always returns 5:

// unit test code
ShimMyClass.MyMethod = () =>5;

모든 인스턴스에 대한 인스턴스 메서드Instance methods (for all instances)

정적 메서드와 마찬가지로, 모든 인스턴스에 대해 인스턴스 메서드를 shim할 수 있습니다.Similarly to static methods, instance methods can be shimmed for all instances. 이러한 shim을 연결할 속성은 혼동을 피하기 위해 AllInstances라는 중첩된 형식에 배치됩니다.The properties to attach those shims are placed in a nested type named AllInstances to avoid confusion. 예를 들어 인스턴스 메서드 MyMethod를 포함하는 MyClass 클래스가 있다고 가정합니다.For example, given a class MyClass with an instance method MyMethod:

// code under test
public class MyClass {
    public int MyMethod() {
        ...
    }
}

인스턴스에 관계없이 항상 5를 반환하는 shim을 MyMethod에 연결할 수 있습니다.You can attach a shim to MyMethod that always returns 5, regardless of the instance:

// unit test code
ShimMyClass.AllInstances.MyMethod = () => 5;

ShimMyClass의 생성된 형식 구조는 다음 코드와 같습니다.The generated type structure of ShimMyClass looks like the following code:

// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
    public static class AllInstances {
        public static Func<MyClass, int>MyMethod {
            set {
                ...
            }
        }
    }
}

이 경우 Fakes는 런타임 인스턴스를 대리자의 첫 번째 인수로 전달합니다.Notice that Fakes passes the runtime instance as the first argument of the delegate in this case.

단일 런타임 인스턴스에 대한 인스턴스 메서드Instance methods (for one runtime instance)

호출 수신자에 따라 다른 대리자가 인스턴스 메서드를 shim할 수도 있습니다.Instance methods can also be shimmed by different delegates, based on the receiver of the call. 이렇게 하면 동일한 인스턴스 메서드가 형식 인스턴스별로 다른 동작을 수행할 수 있습니다.This enables the same instance method to have different behaviors per instance of the type. 이러한 shim을 설정하는 속성은 shim 형식 자체의 인스턴스 메서드입니다.The properties to set up those shims are instance methods of the shim type itself. 인스턴스화된 각 shim 형식은 shim된 형식의 원시 인스턴스에도 연결됩니다.Each instantiated shim type is also associated with a raw instance of a shimmed type.

예를 들어 인스턴스 메서드 MyMethod를 포함하는 MyClass 클래스가 있다고 가정합니다.For example, given a class MyClass with an instance method MyMethod:

// code under test
public class MyClass {
    public int MyMethod() {
        ...
    }
}

첫 번째 shim 형식은 항상 5를 반환하고 두 번째 shim 형식은 항상 10을 반환하도록 MyMethod의 shim 형식 두 개를 설정할 수 있습니다.We can set up two shim types of MyMethod such that the first one always returns 5 and the second always returns 10:

// unit test code
var myClass1 = new ShimMyClass()
{
    MyMethod = () => 5
};
var myClass2 = new ShimMyClass { MyMethod = () => 10 };

ShimMyClass의 생성된 형식 구조는 다음 코드와 같습니다.The generated type structure of ShimMyClass looks like the following code:

// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
    public Func<int> MyMethod {
        set {
            ...
        }
    }
    public MyClass Instance {
        get {
            ...
        }
    }
}

shim된 실제 형식 인스턴스는 Instance 속성을 통해 액세스할 수 있습니다.The actual shimmed type instance can be accessed through the Instance property:

// unit test code
var shim = new ShimMyClass();
var instance = shim.Instance;

shim 형식에는 shim된 형식으로의 암시적 변환도 있으므로 일반적으로 shim 형식을 있는 그대로 사용할 수 있습니다.The shim type also has an implicit conversion to the shimmed type, so you can usually simply use the shim type as is:

// unit test code
var shim = new ShimMyClass();
MyClass instance = shim; // implicit cast retrieves the runtime
                         // instance

생성자Constructors

이후 개체에 shim 형식을 연결하기 위해 생성자를 shim할 수도 있습니다.Constructors can also be shimmed in order to attach shim types to future objects. 각 생성자는 shim 형식에서 정적 메서드 Constructor로 노출됩니다.Each constructor is exposed as a static method Constructor in the shim type. 예를 들어 정수를 사용하는 생성자를 포함하는 MyClass 클래스가 있다고 가정합니다.For example, given a class MyClass with a constructor taking an integer:

// code under test
public class MyClass {
    public MyClass(int value) {
        this.Value = value;
    }
    ...
}

Value getter를 호출할 때 생성자의 값에 관계없이 이후의 모든 인스턴스가 -5를 반환하도록 생성자의 shim 형식을 설정합니다.We set up the shim type of the constructor so that every future instance returns -5 when the Value getter is invoked, regardless of the value in the constructor:

// unit test code
ShimMyClass.ConstructorInt32 = (@this, value) => {
    var shim = new ShimMyClass(@this) {
        ValueGet = () => -5
    };
};

각 shim 형식이 두 개의 생성자를 노출합니다.Each shim type exposes two constructors. 새 인스턴스가 필요한 경우 기본 생성자를 사용해야 하고, shim된 인스턴스를 인수로 사용하는 생성자는 생성자 shim에서만 사용해야 합니다.The default constructor should be used when a fresh instance is needed, while the constructor taking a shimmed instance as argument should be used in constructor shims only:

// unit test code
public ShimMyClass() { }
public ShimMyClass(MyClass instance) : base(instance) { }

ShimMyClass의 생성된 형식 구조는 다음 코드와 유사합니다.The generated type structure of ShimMyClass resembles the following code:

// Fakes generated code
public class ShimMyClass : ShimBase<MyClass>
{
    public static Action<MyClass, int> ConstructorInt32 {
        set {
            ...
        }
    }

    public ShimMyClass() { }
    public ShimMyClass(MyClass instance) : base(instance) { }
    ...
}

기본 멤버Base members

기본 형식에 대한 shim을 만들고 기본 shim 클래스의 생성자에 자식 인스턴스를 매개 변수로 전달하여 기본 멤버의 shim 속성에 액세스할 수 있습니다.The shim properties of base members can be accessed by creating a shim for the base type and passing the child instance as a parameter to the constructor of the base shim class.

예를 들어 인스턴스 메서드 MyMethod 및 하위 형식 MyChild를 포함하는 MyBase 클래스가 있다고 가정합니다.For example, given a class MyBase with an instance method MyMethod and a subtype MyChild:

public abstract class MyBase {
    public int MyMethod() {
        ...
    }
}

public class MyChild : MyBase {
}

새로운 ShimMyBase shim을 만들어 MyBase의 shim을 설정할 수 있습니다.We can set up a shim of MyBase by creating a new ShimMyBase shim:

// unit test code
var child = new ShimMyChild();
new ShimMyBase(child) { MyMethod = () => 5 };

기본 shim 생성자에 매개 변수로 전달하면 자식 shim 형식이 암시적으로 자식 인스턴스로 변환됩니다.Note that the child shim type is implicitly converted to the child instance when passed as a parameter to the base shim constructor.

ShimMyChild 및 ShimMyBase의 생성된 형식 구조는 다음 코드와 유사합니다.The generated type structure of ShimMyChild and ShimMyBase resembles the following code:

// Fakes generated code
public class ShimMyChild : ShimBase<MyChild> {
    public ShimMyChild() { }
    public ShimMyChild(Child child)
        : base(child) { }
}
public class ShimMyBase : ShimBase<MyBase> {
    public ShimMyBase(Base target) { }
    public Func<int> MyMethod
    { set { ... } }
}

정적 생성자Static constructors

shim 형식은 형식의 정적 생성자를 shim하는 정적 메서드 StaticConstructor를 노출합니다.Shim types expose a static method StaticConstructor to shim the static constructor of a type. 정적 생성자는 한 번만 실행되므로 형식의 멤버에 액세스하기 전에 shim이 구성되는지 확인해야 합니다.Since static constructors are executed once only, you need to make sure that the shim is configured before any member of the type is accessed.

종료자Finalizers

종료자는 Fakes에서 지원되지 않습니다.Finalizers are not supported in Fakes.

전용 메서드Private methods

Fakes 코드 생성기는 서명에 표시되는 형식, 즉 표시되는 매개 변수 형식 및 반환 형식만 있는 전용 메서드에 대해 shim 속성을 만듭니다.The Fakes code generator creates shim properties for private methods that only have visible types in the signature, that is, parameter types and return type visible.

바인딩 인터페이스Binding interfaces

shim된 형식이 인터페이스를 구현하는 경우 코드 생성기에서 해당 인터페이스의 모든 멤버를 한 번에 바인딩할 수 있는 메서드를 내보냅니다.When a shimmed type implements an interface, the code generator emits a method that allows it to bind all the members from that interface at once.

예를 들어 IEnumerable<int>를 구현하는 MyClass 클래스가 있다고 가정합니다.For example, given a class MyClass that implements IEnumerable<int>:

public class MyClass : IEnumerable<int> {
    public IEnumerator<int> GetEnumerator() {
        ...
    }
    ...
}

Bind 메서드를 호출하여 MyClass에서 IEnumerable<int>의 구현을 shim할 수 있습니다.We can shim the implementations of IEnumerable<int> in MyClass by calling the Bind method:

// unit test code
var shimMyClass = new ShimMyClass();
shimMyClass.Bind(new List<int> { 1, 2, 3 });

ShimMyClass의 생성된 형식 구조는 다음 코드와 유사합니다.The generated type structure of ShimMyClass resembles the following code:

// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
    public ShimMyClass Bind(IEnumerable<int> target) {
        ...
    }
}

기본 동작 변경Changing the default behavior

생성된 각 shim 형식에는 IShimBehavior 인터페이스의 인스턴스가 ShimBase<T>.InstanceBehavior 속성을 통해 포함됩니다.Each generated shim type holds an instance of the IShimBehavior interface, through the ShimBase<T>.InstanceBehavior property. 클라이언트가 명시적으로 shim되지 않은 인스턴스 멤버를 호출할 때마다 동작이 사용됩니다.The behavior is used whenever a client calls an instance member that was not explicitly shimmed.

동작이 명시적으로 설정되지 않은 경우 정적 ShimsBehaviors.Current 속성에서 반환한 인스턴스를 사용합니다.If the behavior has not been explicitly set, it uses the instance returned by the static ShimsBehaviors.Current property. 기본적으로 이 속성은 NotImplementedException 예외를 throw하는 동작을 반환합니다.By default, this property returns a behavior that throws a NotImplementedException exception.

이 동작은 shim 인스턴스의 InstanceBehavior 속성을 설정하여 언제든지 변경할 수 있습니다.This behavior can be changed at any time by setting the InstanceBehavior property on any shim instance. 예를 들어 다음 코드 조각은 아무 작업을 하지 않거나 반환 형식의 기본값, 즉 default(T)를 반환하는 동작으로 shim을 변경합니다.For example, the following snippet changes the shim to a behavior that does nothing or returns the default value of the return type—that is, default(T):

// unit test code
var shim = new ShimMyClass();
//return default(T) or do nothing
shim.InstanceBehavior = ShimsBehaviors.DefaultValue;

정적 ShimsBehaviors.Current 속성을 설정하여 InstanceBehavior 속성이 명시적으로 설정되지 않은 shim된 모든 인스턴스에 대해 전역적으로 동작을 변경할 수도 있습니다.The behavior can also be changed globally for all shimmed instances for which the InstanceBehavior property was not explicitly set by setting the static ShimsBehaviors.Current property:

// unit test code
// change default shim for all shim instances
// where the behavior has not been set
ShimsBehaviors.Current =
    ShimsBehaviors.DefaultValue;

환경 액세스 검색Detecting environment accesses

해당 shim 형식의 정적 속성 BehaviorShimsBehaviors.NotImplemented 동작을 할당하면 정적 메서드를 포함하여 특정 형식의 모든 메서드에 동작을 연결할 수 있습니다.It is possible to attach a behavior to all the members, including static methods, of a particular type by assigning the ShimsBehaviors.NotImplemented behavior to the static property Behavior of the corresponding shim type:

// unit test code
// assigning the not implemented behavior
ShimMyClass.Behavior = ShimsBehaviors.NotImplemented;
// shorthand
ShimMyClass.BehaveAsNotImplemented();

동시성Concurrency

shim 형식은 AppDomain의 모든 스레드에 적용되며 스레드 선호도가 없습니다.Shim types apply to all threads in the AppDomain and don't have thread affinity. 이는 동시성을 지원하는 Test Runner를 사용하려는 경우에 중요한 팩트입니다. shim 형식과 관련된 테스트는 동시에 실행할 수 없습니다.This is an important fact if you plan to use a test runner that support concurrency: tests involving shim types cannot run concurrently. 이 속성은 Fakes 런타임에 의해 적용되지 않습니다.This property is not enforced by the Fakes runtime.

shim 메서드에서 원래 메서드 호출Calling the original method from the shim method

메서드에 전달된 파일 이름의 유효성을 검사한 후 실제로 파일 시스템에 텍스트를 작성하려 한다고 가정합니다.Imagine that we wanted to actually write the text to the file system after validating the file name passed to the method. 이 경우 shim 메서드 중에 원래 메서드를 호출해야 합니다.In that case, we would want to call the original method in the middle of the shim method.

이 문제를 해결하는 첫 번째 방법은 다음 코드와 같이 대리자 및 ShimsContext.ExecuteWithoutShims()를 사용하여 원래 메서드 호출을 래핑하는 것입니다.The first approach to solve this problem is to wrap a call to the original method using a delegate and ShimsContext.ExecuteWithoutShims() as in the following code:

// unit test code
ShimFile.WriteAllTextStringString = (fileName, content) => {
  ShimsContext.ExecuteWithoutShims(() => {

      Console.WriteLine("enter");
      File.WriteAllText(fileName, content);
      Console.WriteLine("leave");
  });
};

또 다른 방법은 shim을 null로 설정하고 원래 메서드를 호출한 다음 shim을 복원하는 것입니다.Another approach is to set the shim to null, call the original method and restore the shim.

// unit test code
ShimsDelegates.Action<string, string> shim = null;
shim = (fileName, content) => {
  try {
    Console.WriteLine("enter");
    // remove shim in order to call original method
    ShimFile.WriteAllTextStringString = null;
    File.WriteAllText(fileName, content);
  }
  finally
  {
    // restore shim
    ShimFile.WriteAllTextStringString = shim;
    Console.WriteLine("leave");
  }
};
// initialize the shim
ShimFile.WriteAllTextStringString = shim;

제한 사항Limitations

.NET 기본 클래스 라이브러리 mscorlibSystem의 일부 형식에서는 shim을 사용할 수 없습니다.Shims cannot be used on all types from the .NET base class library mscorlib and System.

참고 항목See also