次の方法で共有


スタブを使用して単体テストでアプリケーションの各部分を相互に分離する

スタブの型は、呼び出す他のコンポーネントからテストするコンポーネントを分離することを簡単にするために Microsoft の偽造項目のフレームワークが提供する第 2 の方法の 1 種類です。スタブがテスト中に別のコンポーネントの場所を受け取る、コードです。スタブを使用する利点は、テストを簡単に記述できるようにする一貫した結果を返します。および他のコンポーネントがまだ働かなくてもテストを実行できます。

偽造アイテムのビューとすぐに、スタート ガイドでは、Microsoft Fakes を使用したテストでのコードの分離を参照してください。

インターフェイスのみ使用するようにスタブを使用するには、アプリケーションの他の部分を参照するように、コンポーネント、クラスを記述する必要があります。これは 1 部での変更を別の変更を必要とする可能性が低いため加えると適切なデザイン方法です。テストごとに、実際のコンポーネントのスタブを置き換えることができます。

図では、構成 StockAnalyzer は、テストする。これは通常、別のコンポーネント、RealStockFeed を使用します。ただし、RealStockFeed はメソッドが呼び出されるたびに StockAnalyzer をテストすることが困難になる異なる結果を返します。テストの実行中に、別のクラスとして、StubStockFeed 置き換えます。

Real クラスと Stub クラスは 1 つのインターフェイスに準拠しています。

スタブが、コードをこのように構成することに依存するため、通常、別の場所からアプリケーションの 1 部分を分離するためにスタブを使用します。使用するコントロールに、System.dll など、他のアセンブリと分離は、通常、shim を使用します。「shim を使用して単体テストでアプリケーションを他のアセンブリから分離する」を参照してください。

要件

  • Visual Studio Ultimate

このトピックの内容

スタブを使用する方法

Hh549174.collapse_all(ja-jp,VS.110).gif依存関係の挿入のデザイン

スタブを使用するには、アプリケーションで各種コンポーネントが相互に依存しないが、インターフェイス定義だけに依存するように設計されている。コンパイル時に合わせて代わりに、コンポーネントが実行時に接続されます。このパターンは、変更が反映され構成の境界を越えてが多くなるため、信頼性が高く、更新できるソフトウェアを行うことができます。これは、スタブを使用していない場合でもそれに従うことをお勧めします。新しいコードを記述する場合は、パターン 依存関係の挿入 に従うことが簡単です。既存のソフトウェアのテストを作成した場合、リファクタリングする必要があります。これは実用的でない場合は、shim を代わりに使用することもできます。

目的を提供する例、図の 1 のこの説明を開始します。クラスの StockAnalyzer は株相場を読み取り、興味のある結果を生成します。これにより、テスト対象のあるパブリック メソッドがあります。単純にするには、これらのメソッドのうち 1 つが、特定の共有現在の価格を報告する単純な 1 を確認します。ここでは、そのメソッドの単体テストを記述する必要があります。テストの最初の草稿を次に示します。:

        [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

このテストとの 1 種類の問題は、明白です: 株相場のは異なり、通常、アサーションは失敗します。

別の問題は 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 のステートメントの別のコンポーネントのクラスを、ようにしてください。代わりに、変数とパラメーターは、インターフェイスで宣言する必要があります。コンポーネントのインスタンスは、コンポーネントのコンテナーによってのみ作成する必要があります。

    "コンポーネントによって" 今回はクラスのクラスか、または、まとめて開発および更新するグループを意味します。通常は、コンポーネントは 1 種類の Visual Studio プロジェクトのコードです。ユーザーが同時に更新されるため 1 のコンポーネント内のクラスを分離することはあまり重要です。

    また、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(ja-jp,VS.110).gifスタブの生成]

で、使用する他のコンポーネントからテストするクラスを分離します。アプリケーションをより堅牢な機能と柔軟性が高くするだけでなく、分離は、インターフェイスの実装をテスト用にぶつけるためにコンポーネントを接続できるようにします。

通常の方法でクラスとしてスタブを簡単に作成できます。ただし、Microsoft の偽造項目は、テストの最も適切な動的スタブを作成する方法を示します。

スタブを使用するには、まずインターフェイス定義の型のスタブを生成する必要があります。

アセンブリの) 項目の追加

  1. ソリューション エクスプローラーで、単体テスト プロジェクトの [参照] を展開します。

    • Visual Basic で作業している場合、参照一覧を表示するには、ソリューション エクスプローラーのツール バーの [すべてのファイルを表示] を選択する必要があります。
  2. 、スタブを作成するインターフェイス定義を含むアセンブリを選択します。

  3. ショートカット メニューで、[Fakes アセンブリに追加] を選択します。

Hh549174.collapse_all(ja-jp,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 の) 項目を入力します。スタブ クラス名はプレフィックスとして "" Fakes.Stubのインターフェイス名から派生する追加されて、パラメーターの型名です。

スタブは、プロパティの getter および setter、イベント、およびジェネリック メソッドに対して作成されます。

Hh549174.collapse_all(ja-jp,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(ja-jp,VS.110).gifメソッド

例に示すように、メソッドはスタブ クラスのインスタンスにデリゲートをアタッチしてぶつけるできます。スタブの型の名前は、メソッドとパラメーターの名前から派生します。たとえば、IMyInterface の次のインターフェイスと MyMethodに用意されているメソッド:

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

ここでは MyMethod に 1 を常に返すスタブをアタッチします:

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

関数にスタブを提供しない場合) 項目は戻り値の型の既定値を返す関数を生成します。数値の場合、既定値は 0 で、クラス型には、null (C#) または Nothing (Visual Basic) になります。

Hh549174.collapse_all(ja-jp,VS.110).gifプロパティ

プロパティの getter および setter が別のデリゲートとして公開されており、個別にぶつかることができます。たとえば、IMyInterfaceの Value のプロパティを検討する:

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

ここでは Value の getter および setter に自動プロパティをシミュレートするためにデリゲートをアタッチします:

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

プロパティの setter または getter にスタブ メソッドを提供する)、項目はスタブ プロパティは、単純変数のように機能するように、値を格納するスタブを生成します。

Hh549174.collapse_all(ja-jp,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(ja-jp,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(ja-jp,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 ;
  

仮想メソッドのデリゲートを指定しない場合、項目) が既定の動作を提供したり、基本クラスのメソッドを呼び出します。基本メソッドを呼び出すようにするには 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 のプロパティによって返されるインスタンスを使用します。既定では、このプロパティは 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(ja-jp,VS.110).gifガイダンス

Visual Studio 2012 –の章の 2 番目の一連の配信のためのテスト: 単体テスト: 内部のテスト

参照

概念

Microsoft Fakes を使用したテストでのコードの分離