mockitoが動かない!

Hadoopを使って真面目な集計をするので、テストの真面目に書くことになりました。
mockitoつかってテストするよ(`・ω・´)

適当なMapper

package mapreduceWithMockito;

import java.io.IOException;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class SampleMapper extends Mapper<Object, Text, Text, Text>{
	private Text mapkey = new Text();
	private Text mapvalue = new Text();
	
	protected void map(Object key, Text value, Context context)
			throws IOException ,InterruptedException {
		for (int i = 0 ; i < 5 ; i++) {
			mapkey.set(Integer.toString(i));
			mapvalue.set(Integer.toString(i));
			
			// 1つのMapperで、同じWritableオブジェクトを利用して複数のレコードを出力
			context.write(mapkey, mapvalue);
		}
	}

}

コメントにあるとおり、同じTextを使いまわして出力する。
オブジェクト生成のオーバーヘッドがなくなるとか何とか。
本当かどうかは知らない。

Mapper用の適当なテストコード

package mapreduceWithMockito;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import java.io.IOException;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.mockito.internal.debugging.MockitoDebuggerImpl;
import org.testng.annotations.Test;

@Test
public class SampleMapperTest {
	public void mapTest() throws IOException, InterruptedException {
		Mapper<Object, Text, Text, Text>.Context mockContext = mock(Context.class);
		SampleMapper mapper = new SampleMapper();

		// key, valueは適当
		mapper.map(null, null, mockContext);

		verify(mockContext).write(new Text("0"), new Text("0"));
		verify(mockContext).write(new Text("1"), new Text("1"));
		verify(mockContext).write(new Text("2"), new Text("2"));
		verify(mockContext).write(new Text("3"), new Text("3"));
		verify(mockContext).write(new Text("4"), new Text("4"));
	}
}

TestNGを使ってるのは趣味です。


まぁ、失敗するはずもなく、

出力

===============================================
Suite1
Total tests run: 1, Failures: 1, Skips: 0
===============================================

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.357 sec <<< FAILURE!

Results :

Failed tests: 
  mapTest(mapreduceWithMockito.SampleMapperTest)

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

工工工エエエェェェ(´д`;)ェェェエエエ工工工
失敗するんですけど。

testng-result.xml

Argument(s) are different! Wanted:
context.write(1, 1);
-> at mapreduceWithMockito.SampleMapperTest.mapTest(SampleMapperTest.java:27)
Actual invocation has different arguments:
context.write(4, 4);
-> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)

引数が違うって怒られてる。
4, 4が呼び出されるんだったら1, 1も呼び出されるよね、JK。

確認する

		new MockitoDebuggerImpl().printInvocations(mockContext);

mapper呼び出しの跡にInvocationの一覧を出力する

確認するの出力

********************************
*** Mockito interactions log ***
********************************
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)

全部おんなじ呼び出しだよ!やったねたえちゃん!!
同じTextオブジェクト使ってるからwriteした値が書き変わっちゃったらしい。
mockitoってシャローコピーしてるのか。。。

もしかしてこれって毎回オブジェクト生成しないとテスト通らないのかなー。。。