Search

Mockito

Mockito는 Mock 객체를 쉽게 만들고, 관리하고, 검증할 수 있는 방법을 제공하는 프레임워크이다. Mock 객체는 실 객체와 비슷하게 동작하지만, 프로그래머가 직접 행동을 관리하는 객체이다.

Mapper, Reducer Test

Mockito를 이용하여 작성한 WordCount 작업의 Mapper, Reducer 테스트 코드는 다음과 같다.
@Test // Mockito 를 통해 테스트를 진행한다. // 객체의 멤버, 메소드의 변경, 호출 등을 관리 및 검증할 수 있는 Mock 객체를 이용한다. public void wordCountMapTestWithMockito1() throws IOException, InterruptedException { /* GIVEN */ // 1. 테스트 할 Mapper 의 객체를 생성한다. WordCount.TokenizeMapper mapper = new WordCount.TokenizeMapper(); // 2. 테스트 할 map 메소드의 인자들을 mock 객체로 생성한다. // key 와 value 는 검증할 필요가 없기 때문에 실제 객체로 생성한다. LongWritable key = new LongWritable(0L); Text value = new Text("dog dog cat owl dog cow cat owl dog"); // map 메소드 내 mapper.word.set, context.write 메소드의 정상 호출 여부를 검증하기 위해 mock 객체로 생성한다. mapper.word = mock(Text.class); Mapper.Context context = mock(Mapper.Context.class); /* WHEN */ // 3. 인자를 전달하여 테스트를 수행한다. mapper.map(key, value, context); /* THEN */ // 4. 테스트 결과를 검증한다. // 메소드 호출 순서를 검증하기 위해선 InOrder 객체를 생성해야 한다. // 정상적인 호출 순서는 word.set - context.write - word.set - context.write - ... 이다. // 이를 검증하기 위해 mapper 의 word 와 context 를 인자로 넘겨 생성한다. // mapper.word 의 접근 제한은 protected 로 설정해야 한다. InOrder inOrder = inOrder(mapper.word, context); // Text 클래스인 mapper.word 의 set 메소드가 먼저 호출됨을 검증한다. inOrder.verify(mapper.word) // eq 메소드를 통해 set 메소드의 인자로 전달되는 String 이 입력값과 동일한지 검증한다. .set(eq("dog")); // Context 클래스인 context 의 write 메소드가 다음으로 호출됨은 검증한다. inOrder.verify(context) // eq 메소드를 통해 set 메소드의 인자로 전달되는 Text(mapper.word)와 IntWritable 이 동일한지 검증한다 .write(eq(mapper.word), eq(new IntWritable(1))); // 이후 반복 inOrder.verify(mapper.word).set(eq("dog")); inOrder.verify(context).write(eq(mapper.word), eq(new IntWritable(1))); inOrder.verify(mapper.word).set(eq("cat")); inOrder.verify(context).write(eq(mapper.word), eq(new IntWritable(1))); inOrder.verify(mapper.word).set(eq("owl")); inOrder.verify(context).write(eq(mapper.word), eq(new IntWritable(1))); } @Test public void wordCountReduceTestWithMockito1() throws IOException, InterruptedException { /* GIVEN */ WordCount.IntSumReducer reducer = new WordCount.IntSumReducer(); Text key = new Text("dog"); List<IntWritable> values = Arrays.asList(new IntWritable(1), new IntWritable(1), new IntWritable(1)); Reducer.Context context = mock(Reducer.Context.class); /* WHEN */ reducer.reduce(key, values, context); /* THEN */ verify(context).write(eq(key), eq(new IntWritable(3))); }
Java
복사

Counter Test

@Test // Mockito 를 사용한 Counter 테스트 public void wordCountCounterTestWithMockito1() throws IOException, InterruptedException { /* GIVEN */ WordCountWithCounter.TokenizeMapper mapper = new WordCountWithCounter.TokenizeMapper(); // Context 와 Counter 의 Mock 객체를 생성한다. // Counter 의 최종 결과는 increment 메소드 호출 횟수를 통해 결정된다. Mapper.Context context = mock(Mapper.Context.class); Counter withCounter = mock(Counter.class); Counter withoutCounter = mock(Counter.class); LongWritable key = new LongWritable(0L); Text value = new Text("dog dog cat owl! dog cow? cat owl! dog"); // context 의 getCounter 메소드가 호출되면 해당 counter 의 Mock 객체를 반환하도록 설정한다. when(context.getCounter(WordCountWithCounter.Word.WITH_SPECIAL_CHARACTER)) .thenReturn(withCounter); when(context.getCounter(WordCountWithCounter.Word.WITHOUT_SPECIAL_CHARACTER)) .thenReturn(withoutCounter); /* WHEN */ mapper.map(key, value, context); /* THEN */ // WITH_SPECIAL_COUNTER 의 increment 메소드는 해당 입력에서 총 3번 호출된다. // increment 메소드가 호출될 때의 인자는 언제나 1로 동일하다. verify(withCounter, times(3)).increment(1); // WITHOUT_SPECIAL_COUNTER 의 increment 메소드는 해당 입력에서 총 6번 호출된다. verify(withoutCounter, times(6)).increment(1); }
Java
복사