気まま研究所ブログ

ITとバイク、思ったことをてきとーに書きます。

Moqでvoidメソッドをテストする

テストをちゃんとできるようにと思ってMoqを使って最近勉強しだしたものの、voidメソッドってどうやってテストすんだ・・・?
となり、2時間ほど悩んだところなんとかできたのでその時の備忘録です。
テストはあまり勉強してこなかったのでもうちょっといい方法あれば教えていただけると幸いです。

テストしたいメソッド

テストしたいメソッドはこんなやつ
Savannah Managerで使ってるやつなんだけども、引数でもらったTelnetクライアントに同じく引数でもらった時間情報を送る処理です。

public static void SendTime(ITelnetClient telnet, TimeInfo timeInfo)
{
    TelnetException.CheckTelnetClient(telnet);

    var minute = Math.Ceiling(timeInfo.Minute * 16.666666666666666666666666666667);
    var stTime = (timeInfo.Day - 1) * 24000 + (timeInfo.Hour * 1000) + (int)minute;
    telnet.WriteLine("st " + stTime.ToString());
}

具体的にやりたいこととしては、ITelnetClientのモック作成し、SendTimeにてそのモックに対してコマンドを含めてWriteLineを実行してもらいます。で、その最終的なコマンドが知りたい。
しかしながら、戻り値がないからどうしたものか。
テストのためにいちいちサーバ付けるのは現実的じゃないし、必ずしもテストを実行する環境によっては結果が異なる可能性もあるので避けたい。

Callbackを用いて実装

結果的にMockのSetupメソッドとCallbackメソッドで実現できました。

[TestMethod()]
public void SendTimeTest()
{
    var timeInfo = new TimeInfo
    {
        Day = 2,
        Hour = 11,
        Minute = 34
    };
    var exp = "st 35567";

    var mock = new Mock<ITelnetClient>();
    mock.Setup(x => x.Connected).Returns(true);

    // ここ
    var actCmd = string.Empty;
    mock.Setup(mr => mr.WriteLine(It.IsAny<string>()))
        .Callback((string cmd) => actCmd = cmd);

    var obj = mock.Object;
    Time.SendTime(obj, timeInfo);

    Assert.AreEqual(exp, actCmd);
}

MockのSetupメソッドでWriteLineメソッドが任意の引数で呼び出される場合の設定を取得し、Callbackメソッドを用いてWriteLineメソッドが呼ばれた際に実行される処理を指定します。
これでWriteLineに渡された引数が取れるのでAssert.AreEqualメソッドで比較できます。

Verifyメソッドを用いて実装

もう一つはVerifyメソッドで呼ばれたかどうかで判定します。

[TestMethod]
public void SendTimeTest()
{
    var timeInfo = new TimeInfo
    {
        Day = 2,
        Hour = 11,
        Minute = 34
    };
    var exp = "st 35567";

    var mock = new Mock<ITelnetClient>();
    mock.Setup(x => x.Connected).Returns(true);

    var obj = mock.Object;
    Time.SendTime(obj, timeInfo);

    // ここ
    mock.Verify(m => m.WriteLine(exp), Times.Once);
}

取得した値でゴニョゴニョする必要がないのであればこれが一番シンプルかもしれません。
Verifyメソッドは指定されたメソッドが引数合わせて呼び出されなかった場合に例外を吐き出します。

今回は結果的に以下が呼び出されなかった場合は例外が出てテストに失敗します。

telnet.WriteLine("st 35567");

Callbackで下手にスコープ広げるよりはこっちのほうがマシかも。シンプルだし。

参考

social.msdn.microsoft.com