単体テストを簡単に Unitils 〜アサーションユーティリティ〜

前回の
blog1.mammb.com

では、Unitils の導入とユーティリティである assertReflectionEquals について見てきました。今回はもうすこし詳しくUnitils のユーティリティについて見ていきます。

並び順を許容する LENIENT_ORDER オプション

assertReflectionEquals ではオプションを指定することにより、ゆるいテストを行うことができます。ゆるい条件でテストを記述しておくことで、テストケースの保守を容易にすることができます。
コレクションなどに含まれる要素を比較したいが、その並び順については考慮する必要が無い場合には、比較モードとして LENIENT_ORDER を指定することでゆるい比較が実現できます。

List<Integer> list = Arrays.asList(3, 2, 1);
assertReflectionEquals(Arrays.asList(1, 2, 3), list, ReflectionComparatorMode.LENIENT_ORDER);

デフォルト値を許容する IGNORE_DEFAULTS オプション

assertReflectionEquals による同値性の判定は、クラスの属性が全て同値であるかを検査しますが、比較モードとして ReflectionComparatorMode.IGNORE_DEFAULTS を指定した場合、オブジェクトであればnull、プリミティブであれば 0 や false といったデフォルト値を比較対象から除外することができます。
以下のように、比較しない属性についてはnullのようなデフォルト値を設定しておくことで、同値性の検査から場外することができます。

User user1 = new User(1, null);
User user2 = new User(1, "John");
assertReflectionEquals(user1, user2, ReflectionComparatorMode.IGNORE_DEFAULTS);

この時、デフォルト値としてnull値などが含まれるオブジェクトはassertReflectionEqualsの第一引数として渡す必要があります。第1引数のオブジェクトの属性情報を元に、第2引数の値と比較を行うためです。たとえば、以下のような User クラスと、それを継承する SubUser クラスがあった場合、

public class User {
    private long id;
    private String name;

    public User(long id, String name) {
        this.id = id;
        this.name = name;
    }
}

public class SubUser extends User {
    private String note;
    
    public SubUser(long id, String name, String note) {
        super(id, name);
        this.note = note;
    }
}

以下のテストケースは成功しますが、

User user = new User(1, "John");
SubUser sub = new SubUser(1, "John", "note");
assertReflectionEquals(user, sub);

以下のテストケースでは失敗します。

assertReflectionEquals(sub, user);

ゆるい検証がデフォルトな assertLenientEquals

assertLenientEquals は前述の LENIENT_ORDER と IGNORE_DEFAULTS がプリセットされた assertReflectionEquals です。assertReflectionEquals に比較モードを指定することなく同様の比較を簡素に記述することができます。

日付の違いを許容する LENIENT_DATES オプション

本家のチュートリアルにて、以下のようにすると、日付の違いは許容されるとありますが、

Date actualDate =   new Date(44444);
Date expectedDate = new Date();
assertReflectionEquals(expectedDate, actualDate, ReflectionComparatorMode.LENIENT_DATES);

現状、以下のように実装しても同じ結果となります。

assertReflectionEquals(expectedDate, actualDate);

なぜかというと、Date は内部で transient の long フィールドで日付の情報を保持していますが、assertReflectionEquals で transient のフィールドが評価されないためです・・。 assertReflectionEquals を使用する場合には注意してください。

プロパティアサーション

org.unitils.reflectionassert.ReflectionAssert クラスには、2つのオブジェクトのプロパティにて比較を行う assertPropertyLenientEquals と assertPropertyReflectionEquals もあります。actual の指定として OGNL (Object Graph Navigation Language)を指定することができます。

User user = new User(1, "John");
assertPropertyReflectionEquals("id", 1, user);

ネストされたプロパティも以下の要領で指定できます。

assertPropertyReflectionEquals("address.city", "First street", user);


コレクションについてもサポートされており、複数のユーザのIDを検証するような場合は以下のように記述できます。

User user1 = new User(1, "John");
User user2 = new User(2, "John");
User user3 = new User(3, "John");
List<User> users = Arrays.asList(user1, user2, user3);
assertPropertyReflectionEquals("id", Arrays.asList(1, 2, 3), users);

プロパティアサーションのその他の用途

プロパティアサーションにも先に紹介したように、ReflectionEquals系と、LenientEquals系のメソッドが用意されており、それぞれ assertPropertyReflectionEquals と assertPropertyLenientEquals が対応します。これらも assertReflectionEquals と同じように使用することができます。


次回はデータベースを使用した機能について見ていきます。

blog1.mammb.com