Share via


스텁을 사용하여 단위 테스트를 위한 응용 프로그램의 여러 부분을 서로 격리

스텁 형식 테스트 호출 하는 다른 구성 요소에서 구성 요소를 쉽게 격리할 수 있도록 Microsoft Fake 프레임 워크를 제공 하는 두 가지 기술 중 하나입니다.Stub은의 다른 구성 요소를 테스트 하는 동안 발생 하는 코드의 작은 부분입니다.테스트를 보다 쉽게 작성 하는 일관 된 결과 반환 하는 스텁을 사용 하 여 있습니다.및 기타 구성 요소는 아직 작동 하지 않는 경우에 테스트를 실행할 수 있습니다.

에서는 개요 및 빠른 시작 안내서 Fake 보려면 Microsoft Fakes를 사용하여 테스트 중인 코드 격리.

스텁을 사용 하려면만 클래스가 아니라 인터페이스를 사용 하 여 응용 프로그램의 다른 부분을 참조할 수 있도록 구성 요소를 작성 해야 합니다.한 파트에서 다른 변경 해야 할 가능성이 쉽게 변경 때문에이 좋은 디자인 사례입니다.테스트에 대 한 실제 구성 요소에 대 한 스텁 대체할 수 있습니다.

다이어그램에서 구성 요소 StockAnalyzer 테스트 하고자 하는 것입니다.일반적으로 다른 구성 요소에서 Realstockfeed를 사용합니다.하지만 Stockanalyzer를 테스트 하기가 어려웠습니다 다른 결과 RealStockFeed 해당 메서드를 호출할 때마다 반환 됩니다.테스트 하는 동안 우리는 서로 다른 클래스 Stubstockfeed를 교체합니다.

Real 및 Stub 클래스는 하나의 인터페이스를 따릅니다.

스텁 코드에이 방식으로 구조화 수 있는지에 의존 하기 때문에, 일반적으로 스텁은에서 다른 응용 프로그램의 한 부분 격리를 사용 합니다.System.dll과 같은 컨트롤 아래에 없는 다른 어셈블리를 격리 시키기에 shim을 일반적으로 사용 합니다.자세한 내용은 shim을 사용하여 단위 테스트를 위한 다른 어셈블리에서 응용 프로그램 격리를 참조하십시오.

요구 사항

  • Visual Studio Ultimate

항목 내용

스텁을 사용 하는 방법

Hh549174.collapse_all(ko-kr,VS.110).gif종속성 주입을 위한 디자인

스텁을 사용 하 여 응용 프로그램이 다양 한 구성 요소 않습니다 서로 의존 하지만 인터페이스 정의에 종속 되도록 설계 됩니다.컴파일 타임에 결합 하는 대신 런타임에 구성 요소 연결 되어 있습니다.이 패턴 변경 내용을 구성 요소에 걸쳐 전파 되지 수 있기 때문에 강력 하 고 쉽게 업데이트 하는 소프트웨어를 확인 하는 데 도움이 됩니다.스텁을 사용 하지 않는 경우에 다음 권장 합니다.새 코드를 작성 하는 경우에 따라 하기 쉬운 되는 종속성 주입 패턴.기존 소프트웨어에 대 한 테스트를 작성 하는 경우이 리팩터링 해야 합니다.경우에 실용적이 지 않을 것 대신 shim을 사용 하 여 할 수도 있습니다.

동기를 부여 예제 다이어그램에는이 토론을 시작 해 보겠습니다.StockAnalyzer 읽기 클래스 가격을 공유 하며 몇 가지 흥미로운 결과 생성.테스트 하려는 일부 공용 메서드가 있습니다.간단 하 게 유지 하려면 단지 해당 메서드의 특정 공유의 현재 가격을 보고는 매우 간단 하나 하나씩 살펴보겠습니다.해당 메서드의 단위 테스트를 작성 하겠습니다.다음은 테스트의 첫 번째 초안입니다.

        [TestMethod]
        public void TestMethod1()
        {
            // Arrange:
            var analyzer = new StockAnalyzer();
            // Act:
            var result = analyzer.GetContosoPrice();
            // Assert:
            Assert.AreEqual(123, result); // Why 123?
        }
    <TestMethod()> Public Sub TestMethod1()
        ' Arrange:
        Dim analyzer = New StockAnalyzer()
        ' Act:
        Dim result = analyzer.GetContosoPrice()
        ' Assert:
        Assert.AreEqual(123, result) ' Why 123?
    End Sub

이 테스트 한 문제는 확실: 공유 가격 차이가 있으며 따라서 어설션이 일반적으로 실패 합니다.

또 다른 문제는 Stockanalyzer가 사용 되는 StockFeed 구성 요소를 계속 개발 중입니다 수 있습니다.첫 번째 초안 테스트 메서드의 코드는 다음과 같습니다.

        public int GetContosoPrice()
        {
            var stockFeed = new StockFeed(); // NOT RECOMMENDED
            return stockFeed.GetSharePrice("COOO");
        }
    Public Function GetContosoPrice()
        Dim stockFeed = New StockFeed() ' NOT RECOMMENDED
        Return stockFeed.GetSharePrice("COOO")
    End Function

즉시이 메서드는 컴파일되지 않을 수 또는 StockFeed 클래스에 아직 완료 되지 않았기 때문에 예외가 발생할 수 있습니다.

인터페이스 주입 모두 이러한 문제를 해결합니다.

주입 인터페이스는 다음과 같은 규칙이 적용 됩니다.

  • 클래스 선언에서 나의 다른 구성 요소에서 코드 응용 프로그램의 모든 구성 요소를 명시적으로 참조 해야는 new 문.대신, 변수 및 매개 변수 인터페이스를 선언 해야 합니다.만 구성 요소의 컨테이너에서 구성 요소 인스턴스를 만들어야 합니다.

    "구성 요소"에서 이런 클래스나 클래스를 개발 하 고 함께 업데이트 그룹에 의미 합니다.일반적으로 구성 요소는 Visual Studio 프로젝트에서 코드입니다.동시에 업데이트 될 수 있기 때문에 하나의 구성 요소 내에서 클래스를 분리 하는 덜 중요 합니다.

    도 비교적 안정 된 플랫폼에서는 System.dll과 같은 클래스에서 구성 요소를 분리 하는 것이 매우 중요 하지 않습니다.이러한 클래스는 모든 인터페이스를 쓰는 코드를 복잡 합니다.

StockAnalyzer 코드는 다음과 같이 인터페이스를 사용 하 여 Stockfeed에서 분리가 따라서 향상 될 수 있습니다.

    public interface IStockFeed
    {
        int GetSharePrice(string company);
    }

    public class StockAnalyzer
    {
        private IStockFeed stockFeed;
        public Analyzer(IStockFeed feed)
        {
            stockFeed = feed;
        }
        public int GetContosoPrice()
        {
            return stockFeed.GetSharePrice("COOO");
        }
    }
Public Interface IStockFeed
    Function GetSharePrice(company As String) As Integer
End Interface

Public Class StockAnalyzer
    ' StockAnalyzer can be connected to any IStockFeed:
    Private stockFeed As IStockFeed
    Public Sub New(feed As IStockFeed)
        stockFeed = feed
    End Sub  
    Public Function GetContosoPrice()
        Return stockFeed.GetSharePrice("COOO")
    End Function
End Class

생성할 때이 예에서 Stockanalyzer는 Istockfeed의 구현을 전달 됩니다.완성 된 응용 프로그램에서 연결 초기화 코드를 수행 합니다.

analyzer = new StockAnalyzer(new StockFeed())

이 연결을 수행 하는 유연한 방법입니다.예를 들어, StockAnalyzer 다양 한 조건에서 Istockfeed의 다른 구현을 인스턴스화할 수 있습니다. 있는 팩터리 개체를 받을 수 있습니다.

Hh549174.collapse_all(ko-kr,VS.110).gif스텁 생성

사용 하는 다른 구성 요소를 테스트 하려는 클래스를 분리 했다는.응용 프로그램 만들기 및 더 강력 하 고 유연한, 분리 된 테스트 스텁 테스트 목적으로 인터페이스를 구현 하는 구성 요소를 연결할 수 있습니다.

단순히 일반적인 방법으로 스텁을 클래스로 작성할 수 있습니다.하지만 Microsoft Fake 모든 테스트에 대 한 가장 적절 한 스텁을 만들 수 있는 동적인 방법을 제공 합니다.

스텁을 사용 하려면 먼저 스텁 형식이 인터페이스 정의에서 생성 해야 합니다.

Fake 어셈블리를 추가합니다.

  1. 단위 테스트 프로젝트의 솔루션 탐색기에서 확장 참조.

    • Visual Basic 작업 하는 경우를 선택 해야 모든 파일 표시 참조 목록을 보려면 솔루션 탐색기 도구 모음에서.
  2. 스텁을 생성 하려는 수 있는 인터페이스 정의가 포함 된 어셈블리를 선택 합니다.

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

Hh549174.collapse_all(ko-kr,VS.110).gif스텁 사용 하 여 테스트를 작성 합니다.

[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);
    }
    ...
}
<TestClass()> _
Class TestStockAnalyzer

    <TestMethod()> _
    Public Sub TestContosoStockPrice()
        ' Arrange:
        ' Create the fake stockFeed:
        Dim stockFeed As New StockAnalysis.Fakes.StubIStockFeed
        With stockFeed
            .GetSharePriceString = Function(company)
                                       Return 1234
                                   End Function
        End With
        ' In the completed application, stockFeed would be a real one:
        Dim componentUnderTest As New StockAnalyzer(stockFeed)
        ' Act:
        Dim actualValue As Integer = componentUnderTest.GetContosoPrice
        ' Assert:
        Assert.AreEqual(1234, actualValue)
    End Sub
End Class

여기 마법 부분 특수 클래스인 StubIStockFeed.참조 된 어셈블리의 모든 공용 형식에 대해 Microsoft Fake 메커니즘 스텁 클래스를 생성합니다.스텁 클래스의 이름을 파생 된 인터페이스의 이름입니다 "Fakes.Stub"로 접두사 및 매개 변수 형식 이름을 추가 합니다.

Getter 및 setter의 속성, 이벤트 및 제네릭 메서드에 대 한 스텁이 생성 됩니다.

Hh549174.collapse_all(ko-kr,VS.110).gif매개 변수 값을 확인

구성 요소에서 다른 구성 요소를 호출할 때 올바른 값 전달을 확인할 수 있습니다.값을 저장 하 고 테스트의 본문에서 확인 하면 어설션이 스텁에 둘 수 있습니다.예를 들면 다음과 같습니다.

[TestClass]
class TestMyComponent
{
       
    [TestMethod]
    public void TestVariableContosoPrice()
    {
     // Arrange:
        int priceToReturn;
        string companyCodeUsed;
        var componentUnderTest = new StockAnalyzer(new StubIStockFeed()
            {
               GetSharePriceString = (company) => 
                  { 
                     // Store the parameter value:
                     companyCodeUsed = company;
                     // Return the value prescribed by this test:
                     return priceToReturn;
                  };
            };
        // Set the value that will be returned by the stub:
        priceToReturn = 345;

     // Act:
        int actualResult = componentUnderTest.GetContosoPrice(priceToReturn);

     // Assert:
        // Verify the correct result in the usual way:
        Assert.AreEqual(priceToReturn, actualResult);

        // Verify that the component made the correct call:
        Assert.AreEqual("COOO", companyCodeUsed);
    }
...}
<TestClass()> _
Class TestMyComponent
    <TestMethod()> _
    Public Sub TestVariableContosoPrice()
        ' Arrange:
        Dim priceToReturn As Integer
        Dim companyCodeUsed As String = ""
        Dim stockFeed As New StockAnalysis.Fakes.StubIStockFeed()
        With stockFeed
            ' Implement the interface's method:
            .GetSharePriceString = _
                Function(company)
                    ' Store the parameter value:
                    companyCodeUsed = company
                    ' Return a fixed result:
                    Return priceToReturn
                End Function
        End With
        ' Create an object to test:
        Dim componentUnderTest As New StockAnalyzer(stockFeed)
        ' Set the value that will be returned by the stub:
        priceToReturn = 345

        ' Act:
        Dim actualResult As Integer = componentUnderTest.GetContosoPrice()

        ' Assert:
        ' Verify the correct result in the usual way:
        Assert.AreEqual(priceToReturn, actualResult)
        ' Verify that the component made the correct call:
        Assert.AreEqual("COOO", companyCodeUsed)
    End Sub
...
End Class

다양 한 종류의 형식 멤버에 대 한 스텁

Hh549174.collapse_all(ko-kr,VS.110).gif메서드

스텁 해제 예제에서 설명한 것 처럼 메서드 스텁 클래스의 인스턴스를 대리자를 연결 하 여 된 될 수 있습니다.스텁 형식의 이름은 메서드 및 매개 변수 이름에서 파생 됩니다.예를 들어, 다음 주어진 IMyInterface 인터페이스와 메서드 MyMethod.

// application under test
interface IMyInterface 
{
    int MyMethod(string value);
}

우리는 stub 첨부 MyMethod 는 항상 1을 반환 합니다.

// unit test code
  var stub = new StubIMyInterface ();
  stub.MyMethodString = (value) => 1;

Fake 스텁 함수를 제공 하지 않는 경우 반환 형식의 기본값을 반환 하는 함수를 생성 합니다.숫자에 대 한 기본값은 0 이며 클래스 형식에 대 한 null (C#) 또는 Nothing (Visual Basic).

Hh549174.collapse_all(ko-kr,VS.110).gif속성

속성 getter 및 setter 별도 대리인으로 노출 되 고 별도로 스텁 해제 된 수 있습니다.예로 Value 속성을 IMyInterface.

// code under test
interface IMyInterface 
{
    int Value { get; set; }
}

Getter와 setter의 하 우리 대리자 연결 Value 자동 속성을 시뮬레이션 합니다.

// unit test code
int i = 5;
var stub = new StubIMyInterface();
stub.ValueGet = () => i;
stub.ValueSet = (value) => i = value;

속성의 getter 또는 setter에 대 한 스텁 메서드를 제공 하지 않으면 스텁 속성에서와 같이 간단한 변수를 사용할 수 있도록 Fake 값을 저장 하는 스텁을 생성 합니다.

Hh549174.collapse_all(ko-kr,VS.110).gif이벤트

이벤트 대리자 필드로 노출 됩니다.결과적으로 모든 빙된 이벤트는 단순히 이벤트의 지원 필드를 호출 하 여 발생할 수 있습니다.다음 스텁 인터페이스를 살펴보겠습니다.

// code under test
interface IWithEvents 
{
    event EventHandler Changed;
}

발생 하는 Changed 이벤트를 우리가 단순히 백업 대리자를 호출:

// unit test code
  var withEvents = new StubIWithEvents();
  // raising Changed
  withEvents.ChangedEvent(withEvents, EventArgs.Empty);

Hh549174.collapse_all(ko-kr,VS.110).gif제네릭 메서드

각 원하는 인스턴스 메서드의 대리자를 제공 하 여 제네릭 메서드 스텁 수 있습니다.예를 들어, 다음 인터페이스는 제네릭 메서드가 포함 된 가정 합니다.

// code under test
interface IGenericMethod 
{
    T GetValue<T>();
}

스텁 테스트를 쓸 수 있는 GetValue<int> 인스턴스화.

// unit test code
[TestMethod]
public void TestGetValue() 
{
    var stub = new StubIGenericMethod();
    stub.GetValueOf1<int>(() => 5);

    IGenericMethod target = stub;
    Assert.AreEqual(5, target.GetValue<int>());
}

코드를 호출 하는 경우 GetValue<T> 기타 인스턴스화를 스텁 단순히 동작을 호출 하는 것입니다.

Hh549174.collapse_all(ko-kr,VS.110).gif가상 클래스의 스텁

앞의 예제에서 인터페이스 스텁은 생성 되었습니다.가상 또는 추상 멤버는 클래스에서 스텁을 생성할 수도 있습니다.예를 들면 다음과 같습니다.

// Base class in application under test
    public abstract class MyClass
    {
        public abstract void DoAbstract(string x);
        public virtual int DoVirtual(int n)
        { return n + 42; }
        public int DoConcrete()
        { return 1; }
    }

이 클래스에서 생성 된 스텁 DoAbstract() 및 DoVirtual(), 있지만 없습니다 Doconcrete()에 대 한 대리자 메서드를 설정할 수 있습니다.

// unit test
  var stub = new Fakes.MyClass();
  stub.DoAbstractString = (x) => { Assert.IsTrue(x>0); };
  stub.DoVirtualInt32 = (n) => 10 ;
  

대리자에 대 한 가상 메서드를 제공 하지 않으면 Fake는 기본 동작을 제공할 수 있습니다 또는 기본 클래스의 메서드를 호출 수 있습니다.기본 메서드를 호출 하려면 설정에서 CallBase 속성:

// unit test code
var stub = new Fakes.MyClass();
stub.CallBase = false;
// No delegate set – default delegate:
Assert.AreEqual(0, stub.DoVirtual(1));

stub.CallBase = true;
//No delegate set - calls the base:
Assert.AreEqual(43,stub.DoVirtual(1));

스텁을 디버깅

스텁 형식이 원활 하 게 디버깅 환경을 제공 하도록 설계 되었습니다.기본적으로 디버거 스텁에 연결 된 사용자 지정 멤버 구현을 직접 단계씩 해야 하므로 생성된 된 코드에서 실행 하도록 지시 합니다.

스텁 제한

  1. 메서드 시그니처 포인터가 지원 되지 않습니다.

  2. 스텁 형식이 가상 메서드 디스패치에 의존 하기 때문에 봉인 된 클래스 또는 정적 메서드 스텁 해제 된 될 수 없습니다.이러한 경우 shim 형식에 설명 된 대로 사용 하shim을 사용하여 단위 테스트를 위한 다른 어셈블리에서 응용 프로그램 격리

스텁의 기본 동작을 변경합니다.

각 생성 된 스텁 형식 인스턴스를 보유 하 고 있는 IStubBehavior 인터페이스 (통해는 IStub.InstanceBehavior 속성).클라이언트는 멤버와 연결 된 사용자 지정 대리자를 호출할 때마다 동작 이라고 합니다.동작을 설정 하지 않은 경우 반환 된 인스턴스를 사용 합니다에 StubsBehaviors.Current 속성입니다.기본적으로이 속성은 throw 된 동작 반환 된 NotImplementedException 예외입니다.

설정 하 여 동작을 언제 든 지 변경할 수 있습니다의 InstanceBehavior 스텁 인스턴스 속성입니다.예를 들어, 다음 코드는 아무 작업도 하지 않습니다 또는 반환 형식의 기본값을 반환 하는 동작 변경: default(T).

// unit test code
var stub = new StubIFileSystem();
// return default(T) or do nothing
stub.InstanceBehavior = StubsBehaviors.DefaultValue;

모든 개체의 동작을 설정 되어 있지 않은 설정 하 여 스텁에 대 한 동작을 전역적으로 변경할 수 있습니다의 StubsBehaviors.Current 속성:

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

외부 리소스

Hh549174.collapse_all(ko-kr,VS.110).gif지침

Visual Studio 2012 2 장 연속 배달 테스트: 단위 테스트: 내부 테스트

참고 항목

개념

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