Microsoft Fakes を使用したテストでのコードの分離Isolating Code Under Test with Microsoft Fakes

Microsoft Fakes では、アプリケーションの別の部分をスタブまたは shim で置き換えることにより、テストするコードを分離できます。Microsoft Fakes help you isolate the code you are testing by replacing other parts of the application with stubs or shims. これらは、テストの制御下にある小さいコードです。These are small pieces of code that are under the control of your tests. テストのコードを分離することにより、テストが失敗した場合に、原因が別の場所ではなくそこにあることを確認できます。By isolating your code for testing, you know that if the test fails, the cause is there and not somewhere else. また、アプリケーションの別の部分がまだ機能していない場合でも、スタブと shim を使用すると、コードをテストできます。Stubs and shims also let you test your code even if other parts of your application are not working yet.

Fakes には 2 種類のフレーバーがあります。Fakes come in two flavors:

  • スタブは、クラスを同じインターフェイスを実装する小さな代用に置き換えます。A stub replaces a class with a small substitute that implements the same interface. スタブを使用するには、各コンポーネントがインターフェイスのみに依存し、その他のコンポーネントには依存しないようにアプリケーションを設計する必要があります To use stubs, you have to design your application so that each component depends only on interfaces, and not on other components. ("コンポーネント" とは、1 つのクラス、または一緒に設計され更新される複数のクラスで、通常は 1 つのアセンブリに格納されるもののことです)。(By "component" we mean a class or group of classes that are designed and updated together and typically contained in an assembly.)

  • shim は、アプリケーションのコンパイル済みコードを実行時に変更します。これにより、指定されたメソッド呼び出しを実行する代わりに、テストで提供される shim コードが実行されるようになります。A shim modifies the compiled code of your application at run time so that instead of making a specified method call, it runs the shim code that your test provides. Shim を使用すると、.NET アセンブリなど、変更できないアセンブリの呼び出しを置き換えることができます。Shims can be used to replace calls to assemblies that you cannot modify, such as .NET assemblies.

    他のコンポーネントに置き換わる FakesFakes replace other components

    RequirementsRequirements

  • Visual Studio EnterpriseVisual Studio Enterprise

スタブ型と shim 型から選択するChoosing between stub and shim types

通常、Visual Studio プロジェクトはコンポーネントと見なされますが、それは、これらのクラスを同時に開発および更新するためです。Typically, you would consider a Visual Studio project to be a component, because you develop and update those classes at the same time. プロジェクトがソリューション内の別のプロジェクトに対して実行する呼び出し、またはプロジェクトが参照する別のアセンブリに対して実行する呼び出しにスタブと shim を使用することを検討します。You would consider using stubs and shims for calls that the project makes to other projects in your solution, or to other assemblies that the project references.

一般的なガイドラインとして、Visual Studio ソリューション内の呼び出しにはスタブを使用し、その他の参照先アセンブリの呼び出しには shim を使用します。As a general guide, use stubs for calls within your Visual Studio solution, and shims for calls to other referenced assemblies. これは、独自のソリューション内では、スタブする際に必要な方法でインターフェイスを定義することによってコンポーネントを分離することが適切な方法であるためです。This is because within your own solution it is good practice to decouple the components by defining interfaces in the way that stubbing requires. ただし、一般的には System.dll などの外部アセンブリは分離されたインターフェイス定義を伴わないので、代わりに shim を使用する必要があります。But external assemblies such as System.dll typically are not provided with separate interface definitions, so you must use shims instead.

その他の考慮事項:Other considerations are:

パフォーマンス。Performance. shim の場合は実行時にコードを書き直すので、処理速度が遅くなります。Shims run slower because they rewrite your code at run time. スタブの場合はこのようなパフォーマンス オーバーヘッドがないので、仮想メソッドの場合と同じように高速です。Stubs do not have this performance overhead and are as fast as virtual methods can go.

静的メソッド、sealed 型。Static methods, sealed types. スタブを使用できるのは、インターフェイスを実装する場合のみです。You can only use stubs to implement interfaces. したがって、静的メソッド、仮想でないメソッド、シールされた仮想メソッド、sealed 型のメソッドなどにはスタブ型を使用できません。Therefore, stub types cannot be used for static methods, non-virtual methods, sealed virtual methods, methods in sealed types, and so on.

内部型。Internal types. スタブと shim はいずれも、アセンブリ属性 InternalsVisibleToAttribute を使用することでアクセス可能になる内部型に使用できます。Both stubs and shims can be used with internal types that are made accessible by using the assembly attribute InternalsVisibleToAttribute.

プライベート メソッド。Private methods. メソッド シグネチャですべての型が参照可能な場合、Shim はプライベート メソッドの呼び出しを置き換えることができます。Shims can replace calls to private methods if all the types on the method signature are visible. スタブは、参照可能なメソッドのみを置き換えることができます。Stubs can only replace visible methods.

インターフェイスと抽象メソッド。Interfaces and abstract methods. スタブは、テストに使用できるインターフェイスおよび抽象メソッドの実装を可能にします。Stubs provide implementations of interfaces and abstract methods that can be used in testing. Shim の場合は、メソッド本体がないため、インターフェイスおよび抽象メソッドをインストルメント化することができません。Shims can't instrument interfaces and abstract methods, because they don't have method bodies.

一般に、コードベース内の依存関係から分離するためにスタブ型を使用することをお勧めします。In general, we recommend that you use stub types to isolate from dependencies within your codebase. これを行うには、インターフェイスの背後にあるコンポーネントを非表示にします。You can do this by hiding the components behind interfaces. Shim 型は、テスト可能な API を提供しないサードパーティのコンポーネントから分離する場合に使用できます。Shim types can be used to isolate from third-party components that do not provide a testable API.

スタブの概要Getting started with stubs

より詳細な説明については、「スタブを使用して単体テストでアプリケーションの各部分を相互に分離する」を参照してください。For a more detailed description, see Using stubs to isolate parts of your application from each other for unit testing.

  1. インターフェイスの挿入Inject interfaces

    スタブを使用するには、アプリケーションの別のコンポーネントのクラスを明示的に示すことがないように、テストするコードを記述する必要があります。To use stubs, you have to write the code you want to test in such a way that it does not explicitly mention classes in another component of your application. "コンポーネント" とは、1 つのクラス、または一緒に設計され更新される複数のクラスで、通常は 1 つの Visual Studio コンポーネントに格納されるもののことです。By "component" we mean a class or classes that are developed and updated together, and typically contained in one Visual Studio project. 変数とパラメーターは、インターフェイスを使用して宣言される必要があります。別のコンポーネントのインスタンスは、渡されるかファクトリを使用して作成される必要があります。Variables and parameters should be declared by using interfaces and instances of other components should be passed in or created by using a factory. たとえば、StockFeed がアプリケーションの別のコンポーネントのクラスである場合、これは不適切であると見なされます。For example, if StockFeed is a class in another component of the application, then this would be considered bad:

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

    代わりに、別のコンポーネントによる実装が可能で、テスト目的でスタブによる実装も可能なインターフェイスを定義します。Instead, define an interface that can be implemented by the other component, and which can also be implemented by a stub for test purposes:

    public int GetContosoPrice(IStockFeed feed)  
    { return feed.GetSharePrice("COOO"); }  
    
    Public Function GetContosoPrice(feed As IStockFeed) As Integer  
     Return feed.GetSharePrice("COOO")  
    End Function  
    
  2. Fakes アセンブリの追加Add Fakes Assembly

    1. ソリューション エクスプローラーで、テスト プロジェクトの参照一覧を展開します。In Solution Explorer, expand the test project's reference list. Visual Basic で作業している場合、参照一覧を表示するには、[すべてのファイルを表示] を選択する必要があります。If you are working in Visual Basic, you must choose Show All Files in order to see the reference list.

    2. インターフェイス (たとえば IStockFeed) が定義されているアセンブリへの参照を選択します。Select the reference to the assembly in which the interface (for example IStockFeed) is defined. この参照のショートカット メニューで、[Fakes アセンブリに追加] をクリックします。On the shortcut menu of this reference, choose Add Fakes Assembly.

    3. ソリューションをリビルドします。Rebuild the solution.

  3. テストで、スタブのインスタンスを構築し、そのメソッドのためのコードを指定します。In your tests, construct instances of the stub and provide code for its methods:

    [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 クラスです。The special piece of magic here is the class StubIStockFeed. 参照アセンブリのそれぞれのインターフェイスに対して、Microsoft Fakes のメカニズムによってスタブ クラスが生成されます。For every interface in the referenced assembly, the Microsoft Fakes mechanism generates a stub class. スタブ クラスの名前はインターフェイスの名前から派生します。プレフィックスとして Fakes.Stub が付き、パラメーターの型名が加わります。The name of the stub class is the derived from the name of the interface, with "Fakes.Stub" as a prefix, and the parameter type names appended.

    また、イベントおよびジェネリック メソッドについて、プロパティの getter および setter に対してもスタブが生成されます。Stubs are also generated for the getters and setters of properties, for events, and for generic methods. 詳細については、「スタブを使用して単体テストでアプリケーションの各部分を相互に分離する」を参照してください。For more information, see Using stubs to isolate parts of your application from each other for unit testing.

shim の概要Getting started with shims

(より詳細な説明については、「shim を使用して単体テストでアプリケーションを他のアセンブリから分離する」を参照してください。)(For a more detailed description, see Using shims to isolate your application from other assemblies for unit testing.)

コンポーネントに DateTime.Now の呼び出しが含まれているとします。Suppose your component contains calls to DateTime.Now:

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

テストの実行中、実際のバージョンでは不都合なことにそれぞれの呼び出しで異なる値が返されるため、Now プロパティに shim を使用します。During testing, you would like to shim the Now property, because the real version inconveniently returns a different value at every call.

shim を使用するためにアプリケーション コードを変更したり、特定の方法を記述したりする必要はありません。To use shims, you don't have to modify the application code or write it a particular way.

  1. Fakes アセンブリの追加Add Fakes Assembly

    ソリューション エクスプローラーで、単体テスト プロジェクトの参照を開き、偽装の対象となるメソッドが格納されているアセンブリへの参照を選択します。In Solution Explorer, open your unit test project's references and select the reference to the assembly that contains the method you want to fake. この例では、DateTime クラスが System.dll にあります。In this example, the DateTime class is in System.dll. Visual Basic プロジェクトの参照を確認するには、[すべてのファイルを表示] を選択します。To see the references in a Visual Basic project, choose Show All Files.

    [Fakes アセンブリに追加] をクリックします。Choose Add Fakes Assembly.

  2. ShimsContext に shim を挿入するInsert a shim in a ShimsContext

    [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);  
                }  
            }  
    }  
    
    <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. パラメーター名がメソッド名に追加されます Parameter names are appended to the method name. (System.Fakes へのアセンブリ参照を追加する必要はありません)。(You don't have to add any assembly reference to System.Fakes.)

    前の例では、静的メソッドに shim を使用しています。The previous example uses a shim for a static method. インスタンス メソッドに shim を使用する場合は、型名とメソッド名の間に AllInstances を記述します。To use a shim for an instance method, write AllInstances between the type name and the method name:

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

参照する 'System.IO.Fakes' アセンブリがありません。(There is no 'System.IO.Fakes' assembly to reference. 名前空間は、shim の作成プロセスによって生成されます。The namespace is generated by the shim creation process. ただし、通常どおり、'using' または 'Import' を使用できます。But you can use 'using' or 'Import' in the usual way.)

また、特定のインスタンス、コンストラクター、およびプロパティに shim を作成することもできます。You can also create shims for specific instances, for constructors, and for properties. 詳細については、「shim を使用して単体テストでアプリケーションを他のアセンブリから分離する」を参照してください。For more information, see Using shims to isolate your application from other assemblies for unit testing.

このセクションの内容In this section

スタブを使用して単体テストでアプリケーションの各部分を相互に分離するUsing stubs to isolate parts of your application from each other for unit testing

shim を使用して単体テストでアプリケーションを他のアセンブリから分離するUsing shims to isolate your application from other assemblies for unit testing

Microsoft Fakes におけるコード生成、コンパイル、および名前付け規則Code generation, compilation, and naming conventions in Microsoft Fakes