読者です 読者をやめる 読者になる 読者になる

Training for D-Day

ブログの内容は個人の見解であり、所属する企業を代表するものではありません。

MSFakesの使い方 -How to Use MSFakes-

※Qiitaにも載せてあります。

C# - MSFakesの使い方 -How to Use MSFakes- - Qiita

MSFakesについて

MSFakesは、MSが出しているMock作成ツールのことです。 Visual Studio Premium/Ulitmate エディションじゃなきゃ使えません。

でも使いたい人は多いらしく、以下のURLで投票できます。 http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2919309-provide-microsoft-fakes-with-all-visual-studio-edi

.NETにはDLL(モジュール)っていう単位があります。他の世界ではパッケージ(Java)とか、コンポーネント(UML)とか言われています。 MSFakesは、DLL毎にMockを作ってくれます。

使ってみる

例えば、現在開発しているアプリケーションの名前を、KStoreとしましょう。 これは例えば、リッチアプリケーションでWPFを利用するとして、どのようなモジュール構成にしますか?

規模によりますが、ここはDDDに従うことにしましょうか。 UIを持つアプリケーションは一般的にレイヤー化アーキテクチャが良いですね。

Image.png

上記モジュール構成をVisual Studio上で作ると以下のようになります。

Image.png

※KStore.Shellは上っ面だけなので、Testは作成しない。

KStore.DomainのShippingCartクラスを以下のようにします。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace KStore.Domain
{
    public class ShoppingCart
    {
        public void AddItem( int itemId )
        {
            _Items.Add(itemId);
        }

        public void DeleteOneItem( int itemId )
        {
            if( _Items.Contains(itemId) )
            {
                _Items.Remove(itemId);
            }
            else
            {
                throw new InvalidOperationException();
            }
        }
       
        public int GetItemCount()
        {
            return _Items.Count;
        }

        private HashSet< int> _Items = new HashSet< int>();
    }
}

ベタですが、UIでShippingCartを利用するコードは以下になります。 ShippingCartを挿入(Injection)されてはいますが、直接使っちゃってます。

using KStore.Domain;
using System.Windows;
using System.Windows.Controls;

namespace KStore.UI
{
    /// <summary>
    /// Interaction logic for ItemList.xaml
    /// </summary>
    public partial class Item : Window
    {
        private ShoppingCart _cart;

        public Item(ShoppingCart cart )
        {
            InitializeComponent();
            _cart = cart;
        }
        
        public void Add_Click(object sender, RoutedEventArgs e)
        {
            // Do something for UI.

            var button = sender as Button;
            _cart.AddItem((int)button.Tag);

            // Do something for UI.
        }


        public void Delete_Click(object sender, RoutedEventArgs e)
        {
            var button = sender as Button;
            _cart.DeleteOneItem((int)button.Tag);
        }
    }
}

KStore.DomainやKStore.UIのUTを作成する最、MSFakesを利用するには、以下のようにします。 Image.png

これを押すと、以下のようにFakesアセンブリへの参照が追加され、Fakesによって自動生成されたアセンブリや設定ファイルが作成されます。 Image.png

これでFakesの利用準備完了です。 ここまでは何の問題もないように思えます。ただ、ビルド時間はかかります。 Fakesを利用する場合、ビルド時にFakesアセンブリを作るので、マシンパワーを必要とします。 ソリューションをパラレルビルドオプションを指定していないにもかかわらず、どうやら並列処理(ビルド?)をしているらしく、fakes.exeが複数立ち上がったります。(タスクマネージャーで確認できます) ちなみにたくさんのFakesを利用するプロジェクトの場合、かなりのビルド時間がかかるようになります。マシンも唸ります。

作成された、KStore.Domain.Fakes.dllをILSpyで見てみましょう。

Image.png

ぬ、ShimとStubという2つのクラスができていますね。 これは一体。

FSFakesには、2種類のMockを作ってくれる機能があります。 ShimとStubそれぞれ説明します。

https://msdn.microsoft.com/ja-jp/library/hh549175.aspx?sentenceGuid=92ebaa46650eeb6218defe69c05d4662#mt6

・スタブは、クラスを同じインターフェイスを実装する小さな代用に置き換えます。 スタブを使用するには、各コンポーネントがインターフ>ェイスのみに依存し、その他のコンポーネントには依存しないようにアプリケーションを設計する必要があります ("コンポーネント" とは、1 つのクラス、または一緒に設計され更新される複数のクラスで、通常は 1 つのアセンブリに格納されるもののことです)。

・shim は、アプリケーションのコンパイル済みコードを実行時に変更します。これにより、指定されたメソッド呼び出しを実行する代わりに、テストで提供される shim コードが実行されるようになります。 Shim を使用すると、.NET アセンブリなど、変更できないアセンブリの呼び出しを置き換えることができます。

なるほど。 スタブは従来のモック(Mock)と同様の定義です。いや、逆か。スタブとかドライバという用語は情報処理の試験にも出てきますよね。昔からある言葉です。 shimは、荒業という感じがします。 MSFakesを利用するならば、旨味はshimにあると言えると思います。

・一般的なガイドラインとして、Visual Studio ソリューション内の呼び出しにはスタブを使用し、その他の参照先アセンブリの呼び出しには shim を使用します。 これは、独自のソリューション内では、スタブする際に必要な方法でインターフェイスを定義することによってコンポーネントを分離することが適切な方法であるためです。 ただし、一般的には System.dll などの外部アセンブリは分離されたインターフェイス定義を伴わないので、代わりに shim を使用する必要があります。

ガイドラインには書いてあります。 Visual Studio ソリューション内の呼び出しにはスタブを使用し、その他の参照先アセンブリの呼び出しには shim を使用します。 ここが重要な勘所です。これを破ってしまうと結構大変な目にあうと思います。shim乱用は避けたほうが無難でしょう。

・一般的に、コードベース内の依存関係から分離するためにスタブ型を使用することをお勧めします。 これを行うには、インターフェイスの背後にあるコンポーネントを非表示にします。 検証可能な API を提供しないサードパーティコンポーネントから分離するために Shim 型を使用できます。

でも、だったら、Fakesアセンブリの追加のときに、shimを作るのかスタブを作るのか聞いてくれたほうが良いのではないかと思いました。 必ず2つとも作っちゃうので、ガイドラインに従わずに使おうと思えば使えちゃうので。

ここでは主にShimにフォーカスします。 Shimを利用する場合には、以下の様なコードが必要になります。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.QualityTools.Testing.Fakes;
using KStore.Domain;
using System.Windows.Controls;
using System.Windows;

namespace KStore.UI.Test
{
    [TestClass]
    public class ItemTest
    {
        [ TestMethod]
        public void Add_Click_should_add_item_to_shopping_cart()
        {
            using ( ShimsContext.Create())
            {
                // Arrange
                KStore.Domain.Fakes. ShimShoppingCart.AllInstances .AddItemInt32 = (ShoppingCart cart, int itemId) =>
                {
                    return;
                };
                KStore.Domain.Fakes. ShimShoppingCart.AllInstances .GetItemCount = (ShoppingCart cart) =>
                {
                    return 1;
                };

                var shoppingCart = new ShoppingCart();
                var item = new Item(shoppingCart);

                // Act
                item.Add_Click( new Button() { Tag = 100 }, null);

                // Assert
                Assert.AreEqual(1, shoppingCart.GetItemCount());
            }
        }
    }
}

注目すべきポイントは、 ShimsContext.Create() ですね。 shimを使いますよという宣言です。このusingを忘れるとひどい目にあい、混乱と無秩序が横行する世の中になります。

shim[class name] というクラスがFakesによって作成され、 [method name + arg1 name + arg2 name + ... ] というメソッドも作成されます。 第1引数はFakesではない本物のクラスのインスタンス、第2引数からは元のメソッドの第1引数です。

わかりづらい。 ちょっと嫌ですよね。

でもまぁ、これを実行すると思ったとおり、item.Add_Click内で、Arrange部分でラムラムしたメソッドが呼ばれます。 ※ラムラム = ラムダ式ラムダ式したの略。

これてにAdd_Clickの純粋なUTという意味ではなんでもありな状態になるんですよね。 カバレッジを通すという意味では非常に役に立ちます。

Visual Studioのテストエクスプローラーからテストを実行できます。 Image.png

まとめ

Fakeを利用する際のメリット・デメリットをまとめます。

デメリット

・Fakesにて、Shimを利用する場合には、ShimsContextでUsingしなければならない。 ・Fakesが作ってくれるMockクラス(特にメソッドの)名前付けが微妙。被らないためなのはわかるけど。 ・ビルド時間がかかるようになる。Fakesの個数が多くなるにつれて、顕著になってくる。 ・Shimを利用するつもりがなくても、勝手に作ってくれちゃうから、勝手に利用できちゃう。

メリット

・Mockを簡単に作ってくれる。

※ちなみにVisual Studio 2015 CTP6 で実験しています。