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

ゆうなんとかさんの雑記帳的な。

Twitterで踊ったり音ゲーしたりしてるあの名前がよくわからない人が書いてるらしいよ。

.NET Frameworkのテストでスタブを使う方法

どうやって実現してるんだろう…
最近は.NETで単体テストの素振りをしています。
スタブやモックを使いたいときは、最初から提供されているFakesを使います。StubとShimの2種類があるのですが、Stubの使いどころがよくわかってないのでShimの使い方だけ。
一応、まずStubで済ませられないか検討する必要があるようですけどね。

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

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

*1これだけでもだいぶテストが楽になります。

Fakesを使う準備

参照しているライブラリごとにFakesを作ることができます。ソリューションエクスプローラーで「参照設定」からFakesを使いたいライブラリを選び、右クリック→「Fakesアセンブリに追加」を選びます。すると、「元の名前空間.Fakes」にStubクラスとShimクラスが誕生します。たとえばSystem.DateTimeクラスの場合は「System.Fakes.DateTimeShim」というクラスができています。

Shimの使い方

Shimを使うと、そのクラスが持っているプロパティやメソッドが返す値を乗っ取ることができます。テストメソッド内で、

[TestMethod]
public void TestMethod()
{
    // このusingスコープ内でしか使えないので注意
    using(ShimsContext.Create())
    {
        DateTimeShim.NowGet = () => new DateTime(2014, 1, 1); // DateTime.Nowの挙動を乗っ取る
        var now = DateTime.Now;                               // いつテストをやっても2014年1月1日
        Assert.AreEqual(1, now.Month);                        // テストにとおる
    }
}

というふうにすると、クラスメソッドやクラスプロパティの挙動を乗っ取ることができます。インスタンスのプロパティやメソッドは

System.IO.Fakes.StreamWriterShim.AllInstances.ReadToEnd = () => "これはテストです";

というふうにAllInstancesを間に挟むと乗っ取れます。

メソッドが呼ばれたかどうか確認する方法

[TestMethod]
public void TestMethod()
{
    using(ShimsContext.Create())
    {
        bool isCalled = false;
        DateTimeShim.NowGet = () => { isCalled = true; return new DateTime(2014, 1, 1); };
        var now = DateTime.Now;
        Assert.IsTrue(isCalled);
    }
}

とりあえずこうすればisCalledが呼ばれるかどうかテストすることができます。回数が重要ならboolじゃなくてintにして、呼ばれる度にインクリメントするようにすればいいと思います。

*1:よくわかってないせいか、ShimがStubの上位互換に見えるんですが…