Moles - .NETのモック・スタブフレームワーク

ユニットテストなどではテスト対象外の振る舞いをテスト目的に合わせるためにモックやスタブ等を作成することがあります。
.NET開発でこの様なモックやスタブを簡単に作成するフレームワークMicrosoft ResearchのMolesがあります。
このMolesは以前に紹介したPexにも含まれています。
Molesを使用すれば設計対象範囲外(例えば.NETフレームワークのコアライブラリなど)も含めて、既存のクラスのフィールド(プロパティ)やメソッド単位でデリゲートを使用して振る舞いを置き換えることができます。
例えば

    public class MolesTest
    {
        public DateTime GetTheDate()
        {
            Console.WriteLine("OK");
            return DateTime.Now;
        }
    }

このような.NETのコアライブラリのConsoleクラスのWriteLineメソッドやDateTimeクラスのNowプロパティを使用するメソッドで、例えば

  • コンソールに書き出される文字列が"OK"であるかどうか
  • DateTime.Nowが2000年1月1日の場合いの動作

を確認する場合に

        /// <summary>
        ///A test for GetTheDate
        ///</summary>
        [TestMethod()]
        [HostType("Moles")]
        public void GetTheDateTest()
        {
            // Console.WriteLine()の呼び出しを
            // デリゲート(ラムダ式)で置き換える
            System.Moles.MConsole.WriteLineString = (str) =>
                {
                    // 引数の文字列が"OK"かどうか
                    Assert.IsTrue(str.Equals("OK"), str);
                };

            // DateTime.Nowの取得を 
            // デリゲート(ラムダ式)で置き換える
            System.Moles.MDateTime.NowGet = () => new DateTime(2000, 1, 1);

            // テスト実行
            MolesTest target = new MolesTest();
            Assert.IsTrue((target.GetTheDate() > new DateTime(2000, 1, 1)), target.GetTheDate().ToString());
        }
    }

この様にすることでConsole.WriteLine()やDateTime.Nowの呼び出しを任意の振る舞いのデリゲート(この例ではラムダ式を使用)で置き換えることができます。
インターフェイスやvirtualなメソッドを持つabstractなクラスについてもスタブとしてインスタンスを作成して、実装のないメソッドにデリゲートを割り当てることでテスト用のためだけに実装クラスを作成しなくても良いです。
例えば

    public interface IMolesTest
    {
        int InterfaceMethod();
    }

この様なIMolesTestインターフェイスを使用する

    public class MolesTest
    {
        private IMolesTest iMole;

        public MolesTest(IMolesTest iMole)
        {
            this.iMole = iMole;
        }

        public int InstanceMethod()
        {
            return iMole.InterfaceMethod();
        }
    }

このようなMolesTestクラスをテストする場合にIMolesTestインターフェイスを実装するクラスを作成しなくても

        /// <summary>
        ///A test for InstanceMethod
        ///</summary>
        [TestMethod()]
        [HostType("Moles")]
        public void InstanceMethodTest()
        {
            // IMolesTestを実装するスタブオブジェクトを生成する
            var stubIMole = new Moles.Moles.SIMolesTest();

            // スタブオブジェクトのInterfaceMethodメソッドの振る舞いを
            // デリゲート(ラムダ式)でセットする
            stubIMole.InterfaceMethod = () => { return -1; };

            // テスト実行
            MolesTest target = new MolesTest(stubIMole);
            Assert.AreEqual(target.InstanceMethod(), -1);
        }
    }

このように実行できます。

Molesを使用するには「Pex and Moles - Isolation and White Box Unit Testing for .NET - Microsoft Research」このページからダウンロードしてインストールします。(PexのパッケージにもMolesは含まれています。Pexを使用する場合には別途Molesは必要ありません)
上記のCosoleやDateTimeは.NETのコアライブラリに含まれています。
.NETのコアライブラリのモック・スタブを作成する場合にはテストプロジェクトに「追加」「新しいアイテム」「XMLファイル」として以下の様に"mscorlib.moles"ファイルを作成します。

<?xml version="1.0" encoding="utf-8" ?>
<Moles xmlns="http://schemas.microsoft.com/moles/2010/">
  <Assembly Name="mscorlib"/>
</Moles>

もし他のライブラリや独自のライブラリのモック・スタブを作成する場合には、そのアセンブリ名をxxxxxとすると"xxxxx.moles"ファイルを

<?xml version="1.0" encoding="utf-8" ?>
<Moles xmlns="http://schemas.microsoft.com/moles/2010/">
  <Assembly Name="xxxxx"/>
</Moles>

としてテストプロジェクトに追加します。
あと、パフォーマンス上の制約によりソリューション外のアセンブリの場合には対象のTypeをアセンブリ属性のMoledTypeで指定する必要があります。
例えばSystem.DateTimeとSystem.Consoleについてのモック・スタブを作成する場合には、テストプロジェクトのPropertiesフォルダの下にあるAssemblyInfo.csに以下のように追加します。

[assembly: Microsoft.Moles.Framework.MoledType(typeof(System.DateTime))]
[assembly: Microsoft.Moles.Framework.MoledType(typeof(System.Console))]

またMolesを有効にするテストメソッドには以下の様にHostType属性として"Moles"を指定します。

        /// <summary>
        ///A test for GetTheDate
        ///</summary>
        [TestMethod()]
        [HostType("Moles")]
        public void GetTheDateTest()
        {
            ....

その他詳しくは「Pex and Moles - Isolation and White Box Unit Testing for .NET - Microsoft Research」このページにある資料を参照してみてください。


[2010-04-17]追記:
生成されるモックおよびスタブは元の名前空間の後ろに"Moles"をつけた名前空間に作成されます。
例えば"System"名前空間のモックやスタブの名前空間は"System.Moles"になります。
モッククラスのクラス名は元のクラス名の前に"M"がつきます。
なので"System.Console"クラスのモッククラスは"System.Moles.MConsole"クラスになります。
スタブクラスのクラス名はオリジナルの名前の前に"S"がつきます。
詳しくは「Microsoft Moles Reference Manual」(英語)を参照してください。


テスティングフレームワークにはマイクロソフトのTesting Framework以外にもNUnit、xUnit.Net、MbUnitにも対応している様です。
詳しくは「Microsoft Moles Reference Manual」(英語)の「Appendix A: Unit Test Framework Extensions」を参照してみてください。