ROBOT PAYMENT TECH-BLOG

株式会社ROBOT PAYMENTのテックブログです

ユニットテスト 見やすいって事は便利だねっ

ども。サブスクペイの中の人、川上です。
イマドキの開発現場でテストコードを書かないケース、あまり見かけないのではないでしょうか。
どうせテストコードを実装するのであれば、後から分かりやすくなるように工夫しましょう。・・・というのが今回のお話です。

サンプルコード

Visual Studio 2022 にて C# クラスライブラリをテストする前提です。
テストフレームワークにはMSTestを使用しています。
テスト対象となるコードには以下を用意しました。単純に引数を足し算するだけのメソッドです。

public class Calculator
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}

DataRow属性によるテストの場合

テストを実装するにあたり多くの場合は引数を変化させながら色々なパターンを検査すると思います。
まずはDataRow属性を使用するケースとして以下のようなテストコードを用意しました。

[TestClass]
public class CalculatorTests
{
    [DataTestMethod]
    [DataRow(11, 22, 33)]
    [DataRow(44, -55, -11)]
    public void AddTest(int x, int y, int expected)
    {
        var target = new Calculator.Calculator();
        var actual = target.Add(x, y);
        Assert.AreEqual(expected, actual);
    }
}

さっそくテストを実行してみましょう。 テストエクスプローラーには以下のように結果が表示されました。

AddTest (11,22,33) のように、テストメソッド名とDataRow属性によって渡された引数が表示されています。

今回はサンプルコードなのですぐにテスト内容が分かりますが、これが自分以外の開発者が実装した超絶複雑怪奇な実装だったとしたら・・・?
そしてテストNGが発生したとしたら・・・!?

まずはどのような意図のテストがNGになったのかを特定するところから始まりますね。
そんな未来の誰かさんを助けるために、テストには分かりやすい表示名を設定したほうが絶対いいのです!!

ということで早速実装してみましょう。
DataRow属性の場合はDisplayNameプロパティに文字列を渡すことでテスト結果の表示名がカスタマイズできます。

[TestClass]
public class CalculatorTests
{
    [DataTestMethod]
    [DataRow(11, 22, 33, DisplayName = "正の数を足すケース")]
    [DataRow(44, -55, -11, DisplayName = "負の数を足すケース")]
    public void AddTest(int x, int y, int expected)
    {
        var target = new Calculator.Calculator();
        var actual = target.Add(x, y);
        Assert.AreEqual(expected, actual);
    }
}

実行すると以下のようになります。

DisplayNameで指定した文字列がテストエクスプローラーに表示されました。
いいですね、俄然見やすくなりました!!ぐれえと!!

DynamicData属性によるテストの場合

DataRow属性は便利なのですが渡せる値は定数やプリミティブ型に限られます。
なのでDTOクラスのインスタンスを渡したい場合などはDynamicData属性を使う必要があります。
DataRow属性の説明で使用したテストコードをDynamicData属性で書き直すと以下のようになります。

[TestClass]
public class CalculatorTests
{
    [DataTestMethod]
    [DynamicData(nameof(GetData))]
    public void AddTest(int x, int y, int expected)
    {
        var target = new Calculator.Calculator();
        var actual = target.Add(x, y);
        Assert.AreEqual(expected, actual);
    }

    public static IEnumerable<object[]> GetData => new List<object[]>
    {
        new object[] { 11, 22, 33 },
        new object[] { 44, -55, -11 },
    };
}

渡す値は同じなので、テストエクスプローラーの結果表示はDataRow属性の例と同じですね。

DynamicData属性の表示名をカスタマイズするには、DynamicDataDisplayNameプロパティを使用します。
DynamicDataDisplayNameプロパティには、以下のシグネチャを持つメソッドの名前を指定します。

public static string 任意の名前(MethodInfo methodInfo, object[] data);

具体的には以下のように書き換えればOKです。

[TestClass]
public class CalculatorTests
{
    [DataTestMethod]
    [DynamicData(nameof(GetData), DynamicDataDisplayName = nameof(GetDisplayName))]
    public void AddTest(int x, int y, int expected, string displayName)
    {
        var target = new Calculator.Calculator();
        var actual = target.Add(x, y);
        Assert.AreEqual(expected, actual);
    }

    public static IEnumerable<object[]> GetData => new List<object[]>
    {
        new object[] { 11, 22, 33, "正の数を足すケース" },
        new object[] { 44, -55, -11, "負の数を足すケース" },
    };

    public static string GetDisplayName(MethodInfo methodInfo, object[] data)
    {
        // AddTestメソッド4番目の引数(=displayName)を表示名にする
        return $"{data[3]}";
    }
}

テストエクスプローラーはこんなカンジ。

んー、いいですねぇ!!

番外編

Assertクラスのメソッドにメッセージを追加することもできます。

[TestClass]
public class CalculatorTests
{
    [DataTestMethod]
    [DataRow(11, 22, 33)]
    [DataRow(44, -55, -11)]
    public void AddTest(int x, int y, int expected)
    {
        var target = new Calculator.Calculator();
        var actual = target.Add(x, y);
        Assert.AreEqual(expected, actual, "足してみるテスト");

        var unecpected = 9999;
        Assert.AreEqual(unecpected, actual, "失敗してみるテスト");
    }
}

テストが成功した場合は何も表示されないのですが、失敗した場合はどこがNGだったのかがテストエクスプローラーで確認できます。

ひとつのテストメソッドで複数の検証をする場合は、NG発生時に備えてメッセージを指定しておいたほうが幸せになれそうです。

ハッピーハッピーユーティー

今回のテックブログでは、テストエクスプローラーでの表示名をカスタイマイズする方法をご紹介しました。
本文中では「未来の誰かさんを助けるため」などと書きましたが、自分で書いたコードであっても半年後に見返すと結構忘れてしまうものなので、未来の自分を助けることにもなると思います。
備えあれば患いなしですよ!!
ではまた!!



We are hiring!!

ROBOT PAYMENTでは一緒に働く仲間を募集しています!!!


www.robotpayment.co.jp