JUnit + EasyMock(6)

以下、まだ書きかけのまま放置中です。放置ぷれい(*´д`*)ハァハァ

去年の12/14ぶりに JUnit+EasyMock についてです。
前回の終りに書いていた引数チェックについての方法を、最近実際に作る機会があったので以下に書いて見ます。

EasyMock ではモックとなるクラスのメソッド引数に対する検証も行なうことが出来ます。
このとき、引数が想定しているものか否かを判断するために ArgumentsMatcher とよばれるものが使用されます。
何も設定を行なっていない場合は、単純にオブジェクトの equals メソッドを使用して比較をおこなう ArgumentsMatcher が設定されています。


標準で用意されている ArgumentsMatcher

EasyMock には標準で以下の ArgumentsMatcher が提供されています。

  • MockControl.ALWAYS_MATCHER

引数の比較を行なわず、つねに true を返す ArgumentsMatcher です。
この ArgumentsMatcher を使用した場合、想定される引数と異なる引数によってメソッドが呼び出された場合でもエラーとなりません。

  • MockControl.EQUALS_MATCHER

引数の比較を引数のクラスが持つ equals メソッドを使用して行ないます。
この ArgumentsMatcher を使用する場合、引数として使用するクラスの equals メソッドが正しく実装されている必要があります。
プリミティブ型が引数として使用されている場合、それらのラッパークラス(boolean ならば Boolean, int ならば Integer)が使用されます。

  • MockControl.ARRAY_MATCHER

引数が配列の場合に Arrays.equals メソッドを使用して比較を行ないます。
Arrays.equals メソッドを使用することにより、配列内の各要素に対して比較を行ないます。


ArgumentsMatcher の設定方法

ArgumentsMatcher は以下の二通りの設定が可能です。

  • すべてのメソッド呼び出しに対して同一の ArgumentsMatcher が使用されるようにデフォルトの ArgumentsMatcher を設定
  • メソッド呼び出しそれぞれに対して ArgumentsMatcher を設定

・デフォルトの ArgumentsMatcher を設定

MockControl mockControl = MockControl.createMock( IBar.class );
mockControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );

上記のように、MockControl の setDefaultMatcher メソッドを使用して設定します。


・メソッド呼び出しそれぞれに対して ArgumentsMatcher を設定

MockControl mockControl = MockControl.createMock( IBar.class );
IBar barMock = (IBar)mockControl.getMock();

barMock.func1( arg1 );
mockControl.setMatcher( MockControl.ALWAYS_MATCHER );
mockControl.setReturnValue( barFunc1Result );

メソッド毎に設定を行なう場合、メソッド呼び出しの直後に MockControl の setMatcher メソッドを使用して設定します。
setMatcher メソッドの呼び出しは、対象メソッドの呼び出し直後に行なう必要があります。


ArgumentsMatcher の自作

標準で用意されている ArgumentsMatcher では機能的に足りないことがあります。
そのような場合には ArgumentsMatcher を自作することで、足りない機能を補うことができます。


・引数が JavaBeans の場合で equals メソッドを実装していない場合
引数が VO や DTO といった JavaBeans の場合に EQUALS_MATCHER 等を使用して比較を行なうには equals メソッドをオーバーライドして正しく実装しておく必要があります。
しかしながら、equals メソッドで等しいとする条件がテストコードの場合とアプリケーションコードの場合とで一致しないことがあったりします。また、equals メソッドを実装する手間も結構面倒だったりします。

このような場合、JavaBeans のプロパティ同士の比較を自動的に行なってくれる ArgumentsMatcher があれば便利です。幸いなことに、JavaBeans のオブジェクト同士を文字列に変換して、比較をおこなえる JUnit Converterというものが まさーるのページというところで公開されているので、これを利用することで JavaBeans のプロパティ値比較による ArgumentsMatcher を簡単に作ることができます。
JUnit Converter を使用すると、JavaBeans のオブジェクトを文字列に変換してくれます。
http://homepage3.nifty.com/masarl/article/junit/string-converter.html こちらからDLできるようです。

JUnit Converter を利用した ArgumentsMatcher のソースコードを以下にのせます。

import junit.extensions.converter.Converter;
import org.easymock.AbstractMatcher;

/**
 * JUnit Extension を利用して引数比較を行う EasyMock の ArgumentsMatcher クラスです。
 */
public class ConverterArgumentsMatcher extends AbstractMatcher {

    /** 比較に使用する Converter */
    private Converter converter;
    
    public ConverterArgumentsMatcher(Converter converter) {
        this.converter = converter;
    }

    
    protected boolean argumentMatches(Object arg0, Object arg1) {
        String arg0Str = converter.toString(arg0);
        String arg1Str = converter.toString(arg1);
        
        return arg0Str.equals(arg1Str);
    }
    protected String argumentToString(Object arg0) {
        return converter.toString(arg0);
    }
}

上記、ConverterArgumentsMatcher を使用した例を以下に示します。

// 引数として使用されるDTO用のConveterを生成
Converter converter = new DefaultConverter();
converter = new BeanConverter(conveter, BarDTO.class)

// 引数比較に Converter を使用する ArgumentsMatcher を生成
ConverterArgumentsMatcher argumentsMatcher = new ConverterArgumentsMatcher(converter);

MockControl mockControl = MockControl.createMock( IBar.class );
IBar barMock = (IBar)mockControl.getMock();

barMock.func3( new BarDTO( 1, "(*´д`*)ハァハァ" ) );
mockControl.setMatcher( arugmentsMatcher );
mockControl.setReturnValue( barFunc3Result );

ConverterArgumentsMatcher を生成したあとは、標準の Matcher と同じように使用するだけです。



・複数の引数が渡される場合に、引数の型によって使用する ArgumentsMatcher を切り替えたい
 上記 ConverterAtgumentsMatcher との組み合わせ(引数が複数で、それぞれがことなる JavaBeans の場合にそれぞれ用に Converter を利用する必要があるため)
複数の ArgumentsMatcher を保持して、引数の型によって動作を切り替える ArgumentsMatcher。引数の型とArgumentsMatcherをマップとして保持。

・ArgumentsMatcher の時点ではチェックしたくない。できない場合
引数に渡された情報を保持しておく ArgumentsMatcher (RecorderArgumentsMatcher)
タイムスタンプの書き込みチェックなどに使用。