最近のアメリカではHappy Holidays!!と言うのが一般的だそうです。
12/25で年始まで休日か・・うらやましい(ぉぃ)
閑話休題。前回で、リファクタリングを行い、
valueに与えられた文字列をそのままmapperがwriteするようにしました。
しかし、これでは仕様と異なることが明白です。
常にINFOを含む行が来るという仮定ならば、これでよいのですが、そうとは限りません。
では、実際にテストしてREDとなることを確認しましょう。
INFOを含む行だけでなく、WARNINGを含む行やERRORを含む行が来ることが予想されるので、追加するテストは、次のようなテストデータを用いればいいと予想できます。
String[] lines = {また、INFOを含む行のみが抽出されるはずなので、context.writeの呼び出しは次のようになるはずです。
"2009-12-14 00:00:26,340 INFO hogehoge @ abcdefg hijklmn opqrstu",
"2009-12-14 00:00:26,341 WARN fugafuga @ 11111111111111111111111",
"2009-12-14 00:00:26,342 ERROR fatal @ exception has occurred",
};
verify(context, new Times(1)).write(new Text(lines[0]), new LongWritable(1));
さらに、writeの呼び出しは一度しか行われない筈なので、次のverifyを追加しましょう。
verify(context, new Times(1)).write(any(Text.class), any(LongWritable.class));
テストメソッド名は testMapExtract_ComplexRows()にしてみました。しかし、anyがimportされてないので、コンパイルが通りません。次のstatic import文を追加して下さい。
import static org.mockito.Matchers.any;これでコンパイルまで通った筈です。
テストメソッドは次のようになります。
/**
* 抽出する行が複数の場合
* @throws Exception 例外発生時
*/
@Test
public void testMapExtract_ComplexRows() throws Exception {
Mapper.Context context = mock(Mapper.Context.class);
String[] lines = {
"2009-12-14 00:00:26,340 INFO hogehoge @ abcdefg hijklmn opqrstu",
"2009-12-14 00:00:26,341 WARN fugafuga @ 11111111111111111111111",
"2009-12-14 00:00:26,341 ERROR fatal @ exception has occurred",
};
LogAnalysisMapper mapper = new LogAnalysisMapper();
mapper.map(null, new Text(line[0]), context);
verify(context, new Times(1)).write(new Text(lines[0]), new LongWritable(1));
verify(context, new Times(1)).write(any(Text.class), any(LongWritable.class));
}
では、実行してみましょう。REDになるはずなのですが・・・。
GREENになってしまいました。
これはおかしいですね。REDになるはずなのにGREENになってしまったということは、テストがどこかおかしいと言うことです。(GREENの理由を明確に説明できるのならばいいのですが、今回はREDを予想していたので明らかにおかしい)
GREENになってしまったということは、context.write();が一度しか呼ばれていないということです。
今のmap()メソッド内では無条件にcontext.write()を呼んでいるので、つまりmapメソッドが一度しか呼ばれていないと言うことですね。
つまり、map()メソッドを引数を変えて複数回呼べばREDになるはずですね。
ということで以下のように変更します。
mapper.map(null, new Text(lines[0]), context);
for (String line : lines) {せっかく複数行のテストデータを用意したのに最初の1行しかテストに用いてなかったようです。
mapper.map(null, new Text(line), context);
}
全行をテストで呼ぶように変更しました。
では気を取り直して、テストを再実行してみます。
ようやく想定通り、REDになりました。
(一つ目はGREENのままです。これも想定通りです)
失敗内容を確認してみましょう。
org.mockito.exceptions.verification.TooManyActualInvocations:呼び出しが多すぎるというエラーになっています。
context.write(
isA(org.apache.hadoop.io.Text),
isA(org.apache.hadoop.io.LongWritable)
);
Wanted 1 time but was 3
at jp.co.littel.hadoop.TestLogAnalysisMapper.testMapExtract_ComplexRows(TestLogAnalysisMapper.java:72)
Caused by: org.mockito.exceptions.cause.UndesiredInvocation:
(以下略)
「想定は1回なのに3回呼ばれた」と言っています。これは想定通りの結果です。
(テストデータは3行なのでmap()メソッドは3回呼ばれる。map()メソッド内部では無条件にcontext.write()メソッドを呼び出すので3回呼ばれてしまう)
では、ソースを修正して、REDをGREENにします。
INFOの場合のみ取り出せばいいので、
if (value.toString().contains("INFO")) {
context.write(value, new LongWritable(1));
}
でよさそうです。
(優秀な開発者の方はこの時点でバグを見つけられると思います。またテストを追加してそれを明らかにしていこうと思います。)
では、実行してみます。
おめでとうございます。すべてGREENになりました!
GREENになったらリファクタリングです。
リファクタリングは次回に。。。
0 件のコメント:
コメントを投稿