Fakesのスタブとシムを使ったテスト方法
Fakesは、Microsoft Researchで研究されていた「Pex and Moles」の中から、MolesだけがVS11betaに組み込まれたものです。
Fakesを使うと、インターフェースや抽象クラスに実装を与えたり(スタブ)、既存のバイナリーに任意の実装を差し込んだり(シム)することができます。Fakesを使うには、Microsoft.QualityTools.Testing.Fakesへの参照設定とusing
の指定が必要です。また、テスト対象を変更したときはリビルドが必要です。なお、MSTestだけでなく他のテスティングフレームワークからも利用可能です。
詳しくはMSDNの「Isolating Unit Test Methods with Microsoft Fakes」(執筆時点では機械翻訳)をご覧ください。
スタブの使い方1:インターフェースに実装を与える
テストしたい製品コードに次のインターフェースがあるとします。
public interface IGreeting { int Hour { get; } string GetGreeting(); }
このインターフェースのGetGreeting()
メソッドに、Fakesのスタブ機能を使って実装を与えるには、次のようにします。
// interface のスタブ [TestCase()] public void Stub使用例_GetGreetingTest01() { var greetStub = new StubIGreeting() { GetGreeting = () => "おはよう", }; // インターフェースの実装をラムダ式で与える Assert.AreEqual("おはよう", greetStub.GetGreeting()); }
もしもインテリセンスにStubIGreeting
が出てこなければ、Microsoft.QualityTools.Testing.Fakesへの参照設定を確かめた上で、リビルドしてみてください。
スタブの使い方2:仮想メソッドや仮想プロパティに別の実装を与える
テストしたい製品コードに次のような仮想プロパティを持ったクラスがあるとします。
public class Greeting : IGreeting { public virtual int Hour { get { return DateTimeOffset.Now.Hour; } } public string GetGreeting() { //(略) } }
このHour
プロパティに、Fakesのスタブ機能を使って別の実装を与えることができます。
// virtual メソッドやプロパティを持つクラスのスタブ [TestCase(5, "おはよう")] public void Stub使用例_GetGreetingTest02(int hour, string expected) { var greetStub = new StubGreeting() { HourGet = () => hour, }; // virtual メソッドやプロパティは、置き換えることができる Assert.AreEqual(expected, greetStub.GetGreeting()); }
ここでは、本来の実装がシステムクロックに依存しているところを、引数で与えた時刻が返されるように実装を置き換えています。
なお、実装として与えるラムダ式の中にAssert文を埋め込めば、Mockのように正しい引数が渡されたかどうかをテストすることも可能です。
シムの使い方:バイナリー中のメソッドなどへのアクセスに、実装を差し挟む
シム(shim)とは、割れ目をふさいだり家具を水平にするために差し込む、薄い木片や金属片のことです。既存の実装との間に挟み込んで、その実装へのアクセスを横取りしてしまいます。非常に強力な機能ですが、その分、テストの実行に時間が掛かりますので、濫用しないように気を付けてください。
上記のGreeting
クラスのHour
プロパティを、テストファーストで書こうと思ったとします。しかし、DateTimeOffset.Now
の値は刻一刻と変わってしまいますから、うまくテストが書けませんね。Fakesのシムの機能を使えば、次のようにできます。
まず、DateTime
構造体が入っているSystemアセンブリを指定してFakesアセンブリを作ります。
プロジェクトの参照設定でSystemを右クリックして[Fakesアセンブリに追加]を選ぶ(上図)と、System.4.0.0.0.FakesなどいくつかのFakesアセンブリが自動生成されます。そうしたら、次のようにShimsContext
を使ってテストコードを記述できます。
// System.DateTime.Now に対するアクセスに、シム(shim)を挟み込む [TestCase()] public void Shim使用例_HourTest() { // Hour プロパティを実装するために、 // DateTimeOffset.Now が特定の値を返してくるようにする using (ShimsContext.Create()) { ShimDateTime.NowGet = () => new DateTime(2012, 3, 20, 21, 32, 43); // using を抜けるまで、DateTime.Now の読み取りは 21:32:43 を返し続ける Assert.AreEqual(21, (new Greeting()).Hour); } }
上の画像の左側に、このテストを含む実行結果が出ています。他のテストはだいたいミリ秒のオーダーで終了しているのに、シムを使ったテストは100ミリ秒のオーダーになっていることに注意してください。シムの機能は強力なので、開発中の製品コードに対しても利用したくなるかもしれませんが、ソースコードがあるときはテスト実行時間の短いスタブ機能を使うようにしましょう。
まとめ
- VS11betaでは、NUnitなどのサードパーティ製テスティングツールも、MSTestと同様に利用できるようになった。
- MSTestは、Expressを含む全エディションに搭載され、asyncメソッドのテストが書きやすくなっている。また、ネイティブコードのテストもできるようになった。
- Fakesは、テスト時に実装を置き換える強力な機能を持っている。