MapReduce 작업은 실무에서 약 5시간이 걸리곤 한다. 작업을 수행하는 도중 런타임 에러가 발생하여 작업이 중단되는 불상사가 일어나지 않게 하기 위해 MapReduce 작업은 클러스터에서 실행하기 전 충분한 테스트를 거쳐야 한다.
MRUnit은 MapReduce의 단위 테스트 프레임워크이다. 현재는 retire된 프로젝트이지만, MapReduce 작업을 정상적으로 테스트 하기엔 문제가 없다.
MRUnit을 사용하기 위해선 다음과 같은 의존성을 추가해주어야 한다.
<dependency>
<groupId>org.apache.mrunit</groupId>
<artifactId>mrunit</artifactId>
<version>1.1.0</version>
<classifier>hadoop2</classifier>
<scope>test</scope>
</dependency>
XML
복사
MapDriver, ReduceDriver Test
WordCount 작업의 테스트 코드는 다음과 같다.
@Test
// Mapper 단위 테스트 1
// Setup을 사용하지 않고 다음과 같이 Test 내에서 MapDriver 를 생성할 수 있다.
public void wordCountMapTest1() throws IOException {
// MapDriver를 통해 테스트 할 Mapper를 생성한다.
new MapDriver<Object, Text, Text, IntWritable>() // 테스트 할 Mapper의 타입
/* GIVEN */
.withMapper(new WordCount.TokenizeMapper()) // 테스트 할 Mapper의 클래스
/* WHEN */
.withInput(new LongWritable(0L), new Text("dog dog cat owl dog cow cat owl dog")) // 테스츠 할 입력
/* THEN */
.withOutput(new Text("dog"), new IntWritable(1))
.withOutput(new Text("dog"), new IntWritable(1))
.withOutput(new Text("cat"), new IntWritable(1))
.withOutput(new Text("owl"), new IntWritable(1))
.withOutput(new Text("dog"), new IntWritable(1))
.withOutput(new Text("cow"), new IntWritable(1))
.withOutput(new Text("cat"), new IntWritable(1))
.withOutput(new Text("owl"), new IntWritable(1))
.withOutput(new Text("dog"), new IntWritable(1))
.runTest();
}
@Test
// Reducer 단위 테스트
// Setup을 사용하여 mapDriver 멤버를 사용할 수 있다.
public void wordCountReduceTest1() throws IOException {
new ReduceDriver<Text, IntWritable, Text, IntWritable>()
/* GIVEN */
.withReducer(new WordCount.IntSumReducer())
/* WHEN */
.withInput(new Text("dog"), Arrays.asList(new IntWritable(1), new IntWritable(1), new IntWritable(1), new IntWritable(1)))
.withInput(new Text("cat"), Arrays.asList(new IntWritable(1), new IntWritable(1)))
.withInput(new Text("owl"), Arrays.asList(new IntWritable(1), new IntWritable(1)))
.withInput(new Text("cow"), Arrays.asList(new IntWritable(1)))
/* THEN */
.withOutput(new Text("dog"), new IntWritable(4))
.withOutput(new Text("cat"), new IntWritable(2))
.withOutput(new Text("owl"), new IntWritable(2))
.withOutput(new Text("cow"), new IntWritable(1))
.runTest();
}
Java
복사
runTest() 메소드를 통해 간단한 테스트케이스를 작성할 수 있다. withOutput 메소드를 통해 테스트를 진행할 경우, 실제값과 기대값의 출력 순서가 동일해야 성공적인 테스트가 된다.
run() 메소드는 List 타입의 결과값을 반환하여 사용자가 직접 결과를 확인할 수 있게 해준다. run() 메소드를 사용한 테스트 코드는 다음과 같다.
MapDriver<Object, Text, Text, IntWritable> mapDriver;
ReduceDriver<Text, IntWritable, Text, IntWritable> reduceDriver;
@Before
// MapDriver 와 reduceDriver 를 테스트 시작 전 생성
public void setUp() {
mapDriver = new MapDriver<>(new WordCount.TokenizeMapper());
reduceDriver = new ReduceDriver<>(new WordCount.IntSumReducer());
}
@Test
// Mapper 단위 테스트 2
// Setup을 사용하여 mapDriver 멤버를 사용할 수 있다.
public void wordCountMapTest2() throws IOException {
// run() 메소드는 List<Pair<K, V>> 타입의 결과물을 반환한다.
/* GIVEN */
List<Pair<Text, IntWritable>> result;
result = mapDriver.withInput(new LongWritable(0L), new Text("dog dog cat owl dog cow cat owl dog")) // 테스츠 할 입력
// runTest 대신 run 함수를 통해 결과를 반환받을 수 있다.
/* WHEN */
.run();
/* THEN */
// result.toString() == [(dog, 1), (dog, 1), (cat, 1), (owl, 1), (dog, 1), (cow, 1), (cat, 1), (owl, 1), (dog, 1)]
}
@Test
// Reduce 단위 테스트 2
// Setup을 사용하여 reduceDriver 멤버를 사용할 수 있다.
public void wordCountReduceTest2() throws IOException {
// run() 메소드는 List<Pair<K, V>> 타입의 결과물을 반환한다.
/* GIVEN */
List<Pair<Text, IntWritable>> result;
result = reduceDriver
.withInput(new Text("dog"), Arrays.asList(new IntWritable(1), new IntWritable(1), new IntWritable(1), new IntWritable(1)))
.withInput(new Text("cat"), Arrays.asList(new IntWritable(1), new IntWritable(1)))
.withInput(new Text("owl"), Arrays.asList(new IntWritable(1), new IntWritable(1)))
.withInput(new Text("cow"), Arrays.asList(new IntWritable(1)))
// runTest 대신 run 함수를 통해 결과를 반환받을 수 있다.
/* WHEN */
.run();
/* THEN */
// result.toString() == [(dog, 4), (cat, 2), (owl, 2), (cow, 1)]
System.out.println(result);
}
Java
복사
MapReduceDriver Test
Map 작업과 Reduce 작업을 동시에 수행하여 테스트 할 수 있도록 제공되는 MapReduceDriver 클래스도 있다. MapReduceDriver 클래스의 runTest 메소드를 사용한 테스트 코드는 다음과 같다.
// MapDriver 와 ReduceDriver 를 합친 Driver
// Mapper 입력 kwy-value, Mapper 출력 - Reducer 입력 key-value, Reducer 출력 key-value 의 총 6개의 제네릭 타입을 선언해주어야 한다.
MapReduceDriver<Object, Text, Text, IntWritable, Text, IntWritable> mapReduceDriver;
@Before
// MapDriver 와 reduceDriver 를 테스트 시작 전 생성
public void setUp() {
mapReduceDriver = new MapReduceDriver<>(new WordCount.TokenizeMapper(), new WordCount.IntSumReducer());
}
@Test
// MapReduce 테스트
// MapReduceDriver 의 runTest 메소드를 사용한다.
public void wordCountMapReduceTest1() throws IOException {
/* GIVEN */
Pair<Object, Text> input = new Pair<>(new LongWritable(0L), new Text("dog dog cat owl dog cow cat owl dog"));
/* WHEN */
mapReduceDriver.withInput(input)
/* THEN */
.withOutput(new Text("dog"), new IntWritable(4))
.withOutput(new Text("cat"), new IntWritable(2))
.withOutput(new Text("owl"), new IntWritable(2))
.withOutput(new Text("cow"), new IntWritable(1))
.runTest();
}
Java
복사
MapReduceDriver 클래스의 run 메소드를 사용한 테스트 코드는 다음과 같다.
MapReduceDriver<Object, Text, Text, IntWritable, Text, IntWritable> mapReduceDriver;
@Before
public void setUp() {
mapReduceDriver = new MapReduceDriver<>(new WordCount.TokenizeMapper(), new WordCount.IntSumReducer());
}
@Test
// MapReduce 테스트
// MapReduceDriver 의 run 메소드를 사용한다.
public void wordCountMapReduceTest2() throws IOException {
/* GIVEN */
List<Pair<Text, IntWritable>> result;
Pair<Object, Text> input = new Pair<>(new LongWritable(0L), new Text("dog dog cat owl dog cow cat owl dog"));
/* WHEN */
result = mapReduceDriver.withInput(input)
.run();
/* THEN */
// result.toString() == [(cat, 2), (cow, 1), (dog, 4), (owl, 2)]
System.out.println(result);
}
Java
복사
Counter Test
Counter의 테스트 또한 가능하다. WordCountWithCounter.java에서 설정한 Counter 테스트 코드는 다음과 같다.
MapDriver<Object, Text, Text, IntWritable> mapDriver;
public void setUp() {
mapDriver = new MapDriver<>(new WordCountWithCounter.TokenizeMapper());
}
@Test
// Mapper Counter 테스트
public void wordCountWithCounterCounterTest() throws IOException {
/* GIVEN */
List<Pair<Text, IntWritable>> result;
result = mapDriver.withInput(new LongWritable(0L), new Text("dog dog cat owl! dog cow? cat owl! dog")) // 테스츠 할 입력
/* WHEN */
.run();
/* THEN */
// getCounter 의 findCounter 메소드를 이용하여 특정 Counter 를 가져올 수 있다.
// Word.WITH_SPECIAL_CHARACTER Counter 와 Word.WITHOUT_SPECIAL_CHARACTER Counter 를 가져온다.
long withSpecial = mapDriver.getCounters().findCounter(WordCountWithCounter.Word.WITH_SPECIAL_CHARACTER).getValue();
long withoutSpecial = mapDriver.getCounters().findCounter(WordCountWithCounter.Word.WITHOUT_SPECIAL_CHARACTER).getValue();
// result.toString() == [(dog, 1), (dog, 1), (cat, 1), (owl!, 1), (dog, 1), (cow?, 1), (cat, 1), (owl!, 1), (dog, 1)]
// withSpecial == 3
// withoutSpecial == 6
assertEquals(withSpecial, 3);
assertEquals(withoutSpecial, 6);
}
Java
복사