티스토리 뷰

Java

[Java] Mockito Framework Spy, ArgumentCaptor

snail voyager 2024. 3. 31. 23:47
728x90
반응형

spy()

spy() 메서드를 사용하면 실제 객체의 일부 기능을 유지하면서 일부만 Mock으로 대체할 수 있습니다.

이는 실제 객체의 일부 메서드 동작을 유지하면서, 특정 메서드 호출에 대한 Mock 동작을 정의할 때 유용합니다.

List list = new LinkedList();
List spy = spy(list);

//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);

//using the spy calls *real* methods
spy.add("one");
spy.add("two");

//prints "one" - the first element of a list
System.out.println(spy.get(0));

//size() method was stubbed - 100 is printed
System.out.println(spy.size());

//optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");

when() 으로 실제 메서드가 stubbing 불가능하다면 doReturn|Answer|Throw() 를 사용한다.

List list = new LinkedList();
List spy = spy(list);

//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

doReturn()

doReturn() 메서드는 when() 구문으로는 처리할 수 없는 특정 상황에서 매우 유용

 

  • final 메서드를 스터빙할 때: Mockito는 기본적으로 final 메서드를 스터빙할 수 없습니다.
  • void 메서드를 스터빙할 때: when() 구문은 void 메서드에 적용할 수 없습니다.
  • 이미 호출된 메서드를 스터빙할 때: when() 구문은 메서드가 호출되기 전에 설정해야 합니다. 이미 호출된 메서드를 나중에 스터빙하려면 doReturn()을 사용해야 합니다.
  • 내부 동작이 복잡한 메서드를 스터빙할 때: 특정한 상황에서는 when() 구문을 사용하면 예외가 발생할 수 있습니다. 이 경우 doReturn()을 사용하면 이러한 예외를 피할 수 있습니다.

final 메서드를 스터빙

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import org.junit.Test;

public class FinalMethodExample {
    @Test
    public void testFinalMethod() {
        // Given
        FinalClass finalClassMock = mock(FinalClass.class);

        // When
        doReturn("final response").when(finalClassMock).finalMethod();

        // Then
        String result = finalClassMock.finalMethod();
        assertEquals("final response", result);
    }
}

final class FinalClass {
    public final String finalMethod() {
        return "original response";
    }
}

 

Void 메서드를 스터빙할 때

void 메서드에 특정 동작을 설정하거나 예외를 던지게 하려면 doReturn(), doThrow(), doNothing(), doAnswer() 등을 사용해야 합니다.

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import org.junit.Test;

public class VoidMethodExample {
    @Test
    public void testVoidMethod() {
        // Given
        SomeClass someClassMock = mock(SomeClass.class);

        // When
        doNothing().when(someClassMock).voidMethod();

        // Then
        someClassMock.voidMethod();
        verify(someClassMock).voidMethod();
    }
}

class SomeClass {
    public void voidMethod() {
        // Original implementation
    }
}

이미 호출된 메서드를 스터빙할 때

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import org.junit.Test;
import java.util.List;

public class AlreadyCalledMethodExample {
    @Test
    public void testAlreadyCalledMethod() {
        // Given
        List<String> mockedList = mock(List.class);

        // 메서드 호출
        mockedList.get(0);

        // When
        doReturn("post-hoc response").when(mockedList).get(0);

        // Then
        String result = mockedList.get(0);
        assertEquals("post-hoc response", result);
    }
}

내부 동작이 복잡한 메서드를 스터빙할 때

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import org.junit.Test;

public class ComplexInternalBehaviorExample {
    @Test
    public void testComplexInternalBehavior() {
        // Given
        SomeClass someClassMock = mock(SomeClass.class);

        // 복잡한 내부 동작으로 인해 `when()`을 사용할 수 없는 경우
        // When
        doReturn("complex response").when(someClassMock).complexMethod();

        // Then
        String result = someClassMock.complexMethod();
        assertEquals("complex response", result);
    }
}

class SomeClass {
    public String complexMethod() {
        // Complex internal logic
        return "original response";
    }
}

@Spy

spy() 메서드 대신에 @Spy로 spy 객체를 생성할 수 있다.

import org.mockito.Spy;

public class ExampleTest {

    @Spy
    Calculator spyCalculator;

    @Test
    public void testSpy() {
        // spyCalculator 객체의 일부 메서드의 동작을 유지하면서 일부 Mock 동작 정의
        doReturn(10).when(spyCalculator).add(2, 3);

        // 테스트
        int result = spyCalculator.add(2, 3);

        // 결과 확인
        assertEquals(10, result);

        // 실제 add 메서드가 호출되었는지 확인
        verify(spyCalculator).add(2, 3);

        // 나머지 add 메서드는 실제 객체의 동작을 유지
        assertEquals(7, spyCalculator.add(3, 4)); // 실제 add 메서드 호출
    }
}

ArgumentCaptor

메서드 호출 시 전달된 인자를 캡처하고 이를 검증하는 데 사용됩니다. 

주로 메서드 호출에 대한 매개변수를 확인하거나 특정 인자에 대한 검증을 수행할 때 유용합니다.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

public class ExampleTest {

    @Test
    public void testAddMethodWithArgumentCaptor() {
        // Mock 객체 생성
        Calculator calculator = mock(Calculator.class);

        // ArgumentCaptor 생성
        ArgumentCaptor<Integer> arg1Captor = ArgumentCaptor.forClass(Integer.class);
        ArgumentCaptor<Integer> arg2Captor = ArgumentCaptor.forClass(Integer.class);

        // 특정 메서드 호출 시 전달되는 인자를 캡처
        calculator.add(2, 3);

        // 캡처한 인자를 검증
        verify(calculator).add(arg1Captor.capture(), arg2Captor.capture());
        
        // 캡처한 인자 값 확인
        assertEquals(2, arg1Captor.getValue());
        assertEquals(3, arg2Captor.getValue());
    }
}

@Captor

@Captor 어노테이션을 필드에 적용하면 Mockito가 자동으로 해당 필드를 초기화하고 적절한 유형의 ArgumentCaptor를 할당합니다.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.Test;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
import org.mockito.ArgumentCaptor;

public class ExampleTest {

    @Captor
    private ArgumentCaptor<Integer> arg1Captor;

    @Captor
    private ArgumentCaptor<Integer> arg2Captor;

    @Test
    public void testAddMethodWithArgumentCaptor() {
        // MockitoAnnotations.initMocks()를 사용하여 @Captor 필드 초기화
        MockitoAnnotations.initMocks(this);

        // Mock 객체 생성
        Calculator calculator = mock(Calculator.class);

        // 특정 메서드 호출 시 전달되는 인자를 캡처
        calculator.add(2, 3);

        // 캡처한 인자를 검증
        verify(calculator).add(arg1Captor.capture(), arg2Captor.capture());
        
        // 캡처한 인자 값 확인
        assertEquals(2, arg1Captor.getValue());
        assertEquals(3, arg2Captor.getValue());
    }
}
728x90
반응형

'Java' 카테고리의 다른 글

[Java] Mustache Template Rendering  (0) 2024.04.03
[Java] Object Mapping Frameworks 성능 비교  (0) 2024.04.02
[Java] Garbage Collector 개념  (0) 2024.02.19
[Java] 마이크로벤치마킹 JMH  (0) 2024.02.17
[Java] JVM  (0) 2024.02.17
반응형
300x250