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

ユニットテストとJavaBeansのsetterの悩み

久しぶり(?)にJavaネタ。
昨日/今日とユニットテストを書いてて、悩んだというか飽きてました。

理由はテストを実行するときにJavaBeansにデータをセットしてたのですが、これがめんどくさい。最初はEclipseの保管機能とかで書いてたのですが、時間が経つにつれてマジで飽きてきました。変にコードの行数を使うし。プロパティが1つとか2つならまだしも、10個あって、それが各テストで設定するパラメータ違うから、中途半端な共通化もできない。例えば以下の様なコードです。これ書いててもうイヤになってきた。
ちなみに外部ファイルで定義するというのも1つの手ですが、基本同じクラス内に収めたい。

    public void test_hogehoge() throws Exception {
        ParentBean hoge = new ParentBean();
        hoge.setId(1);
        hoge.setList(Arrays.asList(new String[]{"hoge","fuga"}));
        ChildBean childBean = new ChildBean();
        childBean.setId(1);
        childBean.setStr("hogehoge");
        hoge.setChildBean(childBean);
    }

もちろん BeanUtils#populate とか考えたのですが、そもそもMapに値を詰める時にコードの行数食うからそれが気に入らない。(というか、変える意味がない)

で、テストコードを短くするためにこんなクラスを考えてみた。

/**
 * Bean作成クラス.
 * <pre>
 * Object[][] params = { { &quot;id&quot;, 1 }, { &quot;str&quot;, &quot;hoge&quot; },
 *         { &quot;hoge&quot;, Arrays.asList(new Integer[] { 1 }) } };
 * Foo bean = TestSupport.createBean(Foo.class, params);
 * </pre>
 * 
 * @param clazz  作成したいBeanのクラス
 * @param params メンバ名とパラメータの配列
 * @return
 * @throws InstantiationException
 * @throws IllegalAccessException
 * @throws SecurityException
 * @throws NoSuchFieldException
 */
public static <T> T createBean(Class<T> clazz, Object[][] params) 
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException{
    Map<Object, Object> map = new HashMap<Object, Object>();
    for(int i = 0 ; i < params.length; i++){
        map.put(params[i][0], params[i][1]);
    }
    T instance = clazz.newInstance();
    for(Field field : getAllFields(clazz)){
        if(!map.containsKey(field.getName())) continue;
        field.setAccessible(true);
        field.set(instance, map.get(field.getName()));
    }
    return instance;
}

private static <T> List<Field> getAllFields(Class<T> clazz){
    Class<? super T> superclass = clazz.getSuperclass();
    List<Field> list = new ArrayList<Field>();
    if(superclass != null && !superclass.isInterface()){
        list.addAll(getAllFields(superclass));
    }
    Field[] declaredFields = clazz.getDeclaredFields();
    for(Field field : declaredFields){
        list.add(field);
    }
    return list;
}

ただ、これだとEclipseの自動補完が使えなかったりするので、ちょっと微妙だよなとも思いつつ、ある程度書いてると自動補完使わないからという割り切り感も出てきてました。
これを書いてる途中に下記のコードでいいんじゃないかなと。

Foo bean = new Foo(){{ setId(2); setList(Arrays.asList(new String[]{"hoge"})); }};

うーん…ベストってなんだろう?