2009年12月16日水曜日

MapReduceをJUnitでTDDで作る(3-Mapper作成(1))

前回の投稿ではEclipse上の環境を構築しました。

今回はいよいよプログラムの作成に入ります。

TDDなので、まずはテストケースから作成します。

  1. Eclipseのパッケージエクスプローラー上で、testフォルダーで右クリック、「新規」→「JUnitテスト・ケース」をクリック。
  2. 「新規JUnit テスト・ケース」ダイアログで「新規 JUnit 4 テスト」を選択、パッケージにパッケージ構造を入力。今回は「jp.co.littel.hadoop」と入力しました。
  3. 名前に「TestLogAnalysisMapper」を入力。「setUp」と「tearDown」、お好みで「コメントの生成」にチェックを入れ「完了」ボタンをクリックします。
  4. 「JUnit4がビルド・パスにありません。追加しますか?」と聞かれるので、「次のアクションを実行」が選択され、「JUnit4ライブラリをビルド・パスに追加」が選択されていることを確認して「OK」ボタンをクリック。
  5. testフォルダー配下のjp.co.littel.hadoopパッケージ内にTestLogAnalysisMapperが追加されました。
さて、いよいよコーディングに入ります!!

まずはテストを作成しましょう。
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 *
 * @author yoshitsugu
 */
public class HadoopGrepMapperTest {
    /**
     * @throws java.lang.Exception
     */
    @Before
    public void setUp() throws Exception {
    }

    /**
     * @throws java.lang.Exception
     */
    @After
    public void tearDown() throws Exception {
    }
    /**
     * 抽出するパターン
     * @throws Exception 例外発生時
     */
    @Test
    public void testMapExtract() throws Exception {
        String[] lines = {
            "2009-12-14 00:00:26,340 INFO  hogehoge @ abcdefg hijklmn opqrstu"
        };
        verify(context, new Times(1)).write(new LongWritable(1), new Text(lines[0]));
        verify(context, new Times(1)).write(new Text(lines[0]), new LongWritable(1));
    }
}

「待てぇ~い!コンパイルが全然通らんではないか。」というお叱りの声が聞こえてきそうです。
TDDは、まず「何をテストしているのか」を意識します。まず、何をテストするのかを書くのです。コンパイルを通すのはその後です。
「ちょっと待て、テストから書くのならJUnitで見慣れたassertEqualsなどのメソッドがあるはずだろう?どこにも見あたらないぞ?」

ここが今回の肝、モックオブジェクトライブラリ mockitoです。
verify(context, new Times(1)).write(new LongWritable(1), new Text(lines[0]));
verify(context, new Times(1)).write(new Text(lines[0]), new LongWritable(1));

に注目して下さい。
ここで、contextはorg.apache.hadoop.mapreduce.Mapper.Contextクラスのモックインスタンスです。

verifyメソッドはcontext.write(KEY, VALUE)メソッドが、「KEYがどんな値で」「VALUEがどんな値で」「何度」呼ばれるかを判別します。

上記の例では、context.writeが、KEY=new LongWritable(1)という値で、VALUE=new Text(lines[0])という値では、1度(1 time)だけ呼ばれれば、テストOKであるという事になります。

では、テストができたので、コンパイルを通せるようにしましょう。

コンパイルエラーとなっている部分をマウスでポイントして、[Ctrl]+[1]キーを押下することで、LongWritableやTextはimport文を追加することができるはずです。

つづいて、contextのコンパイルエラーをなくすために、contextを宣言します。
次の一文をtestMapExtract()メソッドの一文目に追加しましょう。
Mapper.Context context;

そうするとMapperがコンパイルエラーになるはずです。
[Ctrl]+[1]キーでorg.apache.hadoop.mapreduce.Mapperのimport文を追加しましょう。
まだverifyがコンパイルエラーになっていますね。

これはEclipse任せでは解消できないので次のimport static文を追加して下さい。
import static org.mockito.Mockito.verify;


そうすると、再びcontextが初期化されてないのでコンパイルエラーになると思います。
とりあえず、context = nullで初期化しましょう。

ここまでで、コンパイルが一通り通りました。

今回はここまでにします。

1 件のコメント:

  1. お詫び:
    肝心のテストの所、引数を
    verify(context, new Times(1)).write(new Text(lines[0]), new LongWritable(1));
    としないと、複数datanodeで実行した時に結果がおかしくなってしまう事に気がつきました。謹んで訂正いたします。

    返信削除