次の方法で共有


shim を使用して単体テストでアプリケーションを他のアセンブリから分離する

Shim の型 は、第 2 の方法の 1 つがする環境でテスト対象のコンポーネントを分離することを簡単に許可する Microsoft の偽造項目のフレームワークが使用されます。Shim、特定のメソッドにテストの一部として記述するコードに呼び出しを切り替えます。多くのメソッドは、外部条件に依存異なる結果を返しますが、shim は、テストの管理下にあり、任意の呼び出しで一貫性のある結果を返すことができます。これは、テストをより簡単に作成できます。します。

ソリューションの一部ではないアセンブリからコードを分離するために shim を使用します。相互に、ソリューションのコンポーネントを分離するために、スタブを使用することをお勧めします。

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

要件

  • Visual Studio Ultimate

" "を参照してください ビデオ (1h16) : Visual Studio 2012 の偽造項目のテストの非検証可能なコード

このトピックの内容

ここでこのトピックについて示しています:

例: バグの Y2K

Shim を使用する方法

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

  • ShimsContext を使用します。

  • Shim を使用してテストを記述します

さまざまなメソッドの Shim

既定の動作の変更

環境のアクセスの検出

Concurrency

shim のメソッドから元のメソッドを呼び出して

制限事項

例: バグの Y2K

2000 年の 1 年 1 月 1 日の例外をスローするメソッドについて考えてみましょう:

// 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に依存するため、特に問題となるコンピューターのクロックに依存するメソッド、環境、依存非確定的なメソッド。さらに、DateTime.Now ため、スタブは静的プロパティの型は、ここでは使用できません。この問題は、単体テストの分離の問題を示すものです: データベース API を直接呼び出すプログラムは Web サービスと、それぞれのロジックが環境によって異なるため、単体テストなどに比べてと通信します。

これは shim の型を使用する必要がある。Shim の型はユーザー定義のデリゲートに迂回するための機構 .NET のメソッドを提供します。Shim の型は) 項目のジェネレーターでコード生成し、これは shim の型を呼び出すメソッドの新しい実装を指定するには、デリゲートを使用します。

次のテストを DateTime.Now のカスタム実装を提供するために shim の型、ShimDateTimeを使用する方法を示します:

//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 を使用する方法

Hh549176.collapse_all(ja-jp,VS.110).gifアセンブリの) 項目の追加

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

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

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

Hh549176.collapse_all(ja-jp,VS.110).gifShimsContext を使用します。

shim を使用すると、単体テスト フレームワーク ShimsContext のテスト コードを、shim の有効期間を制御するラップする必要があります。これは、これを必要としない場合は、シムは AppDomain がシャットダウンするまで継続します。ShimsContext を作成する最も簡単な方法は、次のコードに示すように Create() の静的メソッドを使用します:

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

正しく各 shim のコンテキストを破棄することが重要です。一般に、登録された shim の適切な削除を確認するために using のステートメント内の ShimsContext.Create を常に呼び出します。たとえば、2000 年 1 月 1 1 常に返すデリゲートと DateTime.Now のメソッドを置き換えるテスト メソッドの shim を登録する場合があります。テスト メソッドで登録された shim をクリア忘れるとテストの実行の後半では、1 月 1 日 2000 ため DateTime.Now の値を常に返す。これは意外、複雑になる場合があります。

Hh549176.collapse_all(ja-jp,VS.110).gifshim を使用してテストを記述します

テスト コードでは、偽造するメソッドの Moles は、迂回を 挿入します。次に例を示します。

[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 がテスト対象アプリケーションに Moles は、迂回 のコードを挿入して機能します。場所に元のメソッドの呼び出しは実際のメソッドを呼び出す代わりに、shim コードが呼び出されるように、システムが Moles は、迂回を実行) 項目に発生します。

Moles は、迂回実行時に作成および削除されることに注意してください。ShimsContextの有効期間内の Moles は、迂回を常に作成する必要があります。これが破棄されたとき、アクティブな削除されると、作成した shim。これを行う最適な方法は、using のステートメント内にあります。

偽造項目の名前空間がないことを示すビルド エラーが発生することもあります。このエラーは、他のコンパイル エラーがない場合に表示されます。そのほかのエラーを修正する場合はそれが削除されます。

さまざまなメソッドの Shim

Shim の型は、独自のデリゲートと .NET のメソッドを、静的メソッドまたは非仮想メソッドが、置き換えることができます。

Hh549176.collapse_all(ja-jp,VS.110).gif静的メソッド

静的メソッドに shim をアタッチするプロパティが shim の型に設定されます。各プロパティにターゲット メソッドにデリゲートをアタッチするために使用できる setter のみです。たとえば、クラスの静的メソッド MyMethodの MyClass を指定する:

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

ここでは MyMethod に 5 を常に返す shim をアタッチすることです:

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

Hh549176.collapse_all(ja-jp,VS.110).gifインスタンス メソッド (すべてのインスタンスの場合)

同様に、静的メソッドとインスタンス メソッドのすべてのインスタンスにくさびを入力できます。これらのプロパティは shim をアタッチする AllInstances という入れ子にされた型に混乱を避けるために設定されます。たとえば、クラスのインスタンス メソッド MyMethodの MyClass を指定する:

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

MyMethod にインスタンスに関係なく 5 を常に返す shim をアタッチできます:

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

ShimMyClass の生成された型の構造体には次のコードのようです:

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

偽造項目がデリゲートの最初の引数としてこの場合、ランタイムのインスタンスを渡すことに注意してください。

Hh549176.collapse_all(ja-jp,VS.110).gifメソッド インスタンスを付けます。1 (二つのランタイム インスタンスの場合)

インスタンスのメソッドは、呼び出しの受信側に基づいて、デリゲートによって、くさびを入力できます。これにより、型のインスタンスごとに異なる動作をするのと同じインスタンス メソッドができます。これらの shim を設定するプロパティが shim の型のインスタンス メソッド自体です。それぞれのインスタンスを作成する shim の型は、くさびを入力型の生インスタンスに関連付けられます。

たとえば、クラスのインスタンス メソッド MyMethodの MyClass を指定する:

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

まず、の 1 つが常に 5 を返し、2 番目は常に 10 を返すように MyMethod の 2 種類の shim の型を設定することが可能です:

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

ShimMyClass の生成された型の構造体には次のコードのようです:

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

実際には、インスタンスのプロパティを使用して型のインスタンスにアクセスできますくさびを入力しました:

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

shim の型にもくさびを入力型への暗黙の変換があります。そのため、次のようになるように、shim の型を使用できます:

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

Hh549176.collapse_all(ja-jp,VS.110).gifコンストラクター

コンストラクターは、将来のオブジェクトに shim の型をアタッチするにはくさびを入力できます。各コンストラクターはとして shim の型の静的コンストラクターとメソッド公開されます。たとえば、クラスに整数を取得するコンストラクターでの MyClass を指定する:

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

これは、値の getter が呼び出されると、今後のインスタンスがコンストラクターの値に関係なく -5 を返すように、コンストラクター shim の型を設定します:

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

各 shim の型に 2 人のコンストラクターを公開することに注意してください。既定のコンストラクターは、引数としてくさびを入力インスタンスを取得するコンストラクターは shim にのみ使用する必要はありませんが、新しいインスタンスが必要なときに使用するです:

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

ShimMyClass に生成された型の構造体 followoing コードに似ています:

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

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

Hh549176.collapse_all(ja-jp,VS.110).gif基本メンバー

基本メンバーの shim のプロパティは基本型の shim を作成し、shim の基本クラスのコンストラクターに子のインスタンスをパラメーターとして渡すことによってアクセスできます。

たとえば、クラスのインスタンス メソッド MyMethod およびサブタイプを指定 MyChildの MyBase :

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

public class MyChild : MyBase {
}

ここでは ShimMyBase の新しい shim の作成によって MyBase の shim を設定することが可能です:

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

ように、パラメーター基本 shim のコンストラクターに渡された場合の子 shim の型が子のインスタンスに暗黙的に変換されていることに注意してください。

ShimMyChild と ShimMyBase の生成された型の構造体には次のコードと似ています:

// 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 { ... } }
}

Hh549176.collapse_all(ja-jp,VS.110).gif静的コンストラクター

Shim の型は、型の静的コンストラクターでくさびを入力するには StaticConstructor 静的メソッドを公開します。静的コンストラクターが実行されない一度ため、型のメンバーにもアクセスする前に、shim のみが構成されていることを確認する必要があります。

Hh549176.collapse_all(ja-jp,VS.110).gifファイナライザー

ファイナライザーは) 項目ではサポートされていません。

Hh549176.collapse_all(ja-jp,VS.110).gifプライベート メソッド

コード ジェネレーター) 項目を表示するを入力する可視定義、ie.. のパラメーターの型と戻り値の型を指定できるプライベート メソッドに対する shim のプロパティを作成します。

Hh549176.collapse_all(ja-jp,VS.110).gifバインディング インターフェイス

くさびを入力型がインターフェイスを実装すると、コード ジェネレーターは、そのインターフェイスのすべてのメンバーをすぐにバインドするようにするメソッドを出力します。

たとえば、クラスにはこの MyClass を指定 IEnumerable<int>を実行します:

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

ここでは、バインドのメソッドを呼び出して、MyClass の IEnumerable<int> の実装にくさびを入力できます:

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

ShimMyClass の生成された型の構造体には次のコードと似ています:

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

既定の動作の変更

生成された各 shim の型は ShimBase<T>.InstanceBehavior のプロパティを使用して IShimBehavior のインターフェイスのインスタンスを保持します。動作は、クライアントが明示的にくさびを入力されていないインスタンス メンバーを呼び出すたびに使用されます。

動作を明示的に設定されていない場合、ShimsBehaviors.Current の静的プロパティから返されたインスタンスを使用します。既定では、このプロパティは NotImplementedException の例外をスローする動作を返します。

この動作は、shim のインスタンスの InstanceBehavior のプロパティを設定して、いつでも変更できます。たとえば、次のスニペットは何も実行せず、は、既定の既定値を返さない動作にある型と shim を変更します (T) :

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

動作には、InstanceBehavior のプロパティが ShimsBehaviors.Current の静的プロパティを設定して明示的に設定しなかったすべてのくさびを入力インスタンスにグローバルに変更できます:

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

環境のアクセスの検出

対応する shim の型の静的プロパティ Behavior へ ShimsBehaviors.NotImplemented の動作を再配置して、すべてのメンバーに動作を、特定の種類の静的メソッドなど、アタッチすることも可能です:

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

Concurrency

Shim の型は AppDomain のすべてのスレッドに適用し、スレッド アフィニティはありません。これは、テスト ランナーを使用するには重要なことです。同時実行をサポートする: shim の型を含むテストを同時に実行できます。このプロパティは、ランタイム) 項目ではない enfored。

shim のメソッドから元のメソッドを呼び出して

これは、実際はメソッドに渡されたファイル名を検証した後、ファイル システムにテキストを書き込む先となることがあるとします。このケースでは、shim のメソッドの途中で元のメソッドを呼び出してする場合。

この問題を解決する一つ目は、次のコードのように、デリゲートと ShimsContext.ExecuteWithoutShims() を使用して元のメソッドに呼び出しをラップすることです:

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

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

もう一つの方法は、shim を null に設定し、元のメソッドを呼び出し、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;

制限事項

Shim は、.NET の基本クラス ライブラリ mscorlib や **[システム]**からすべての型で使用することはできません。

外部リソース

Hh549176.collapse_all(ja-jp,VS.110).gifガイダンス

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

参照

概念

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

その他の技術情報

Peter の処理寮長のブログ: Visual Studio 2012 の Shim

ビデオ (1h16) : Visual Studio 2012 の偽造項目のテストの非検証可能なコード