Spring

[Test] void 메서드 테스트 및 Stubbing (모의 동작 지정하기)

mint* 2024. 4. 10. 19:06
728x90

void 메서드 테스트하기

void 메서드를 테스트하려고하는데 구글링으로 찾기 어려워 정리했다.

 

doNothing()

doNothing().when(mockObject).voidMethod();

void 메서드 호출 시 아무것도 하지 않도록 설정한다.

voidMethod()부분에는 실제 void 메서드 명을 적어주어야한다.

 

doThrow()

doThrow(new RuntimeException()).when(mockObject).voidMethod();

void 메서드 호출 시 특정 예외를 던지도록 설정한다.

 

doCallRealMethod()

doCallRealMethod().when(mockObject).voidMethod();

mock 객체의 void 메서드 호출 시 실제 메서드를 호출한다.

mock 객체의 일부는 실제 동작을 수행하고 싶을 때 위 메서드를 호출한다. 

 

Stubbing (모의 동작 지정하기)

doAnswer

doAnswer(answer).when(mockObject).voidMethod(arguments);

void 메서드의 모의 동작을 설정한다.

메서드 호출 시 실행될 동작을 지정할 수 있다.

answer 인터페이스 구현 객체를 인자로 받는다. 

 

Answer 인터페이스

Answer<Object> answer = new Answer<Object>() {
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return null;
    }
};

Answer 인터페이스는 answer 메서드를 제공하는데, 메서드 호출 시 실행할 동작을 정의한다.

 

invocation(호출) : InvocationOnMock 객체이며, mock 객체의 메서드 호출 정보를 담고 있다.

  • invocation.getArguments() : 메서드에 전달된 인자 반환
  • invocation.getArguments(index) : 특정 index의 인자 반환
  • invocation.callRealMethod() : 실제 메서드 호출
가로챈 void 메서드의 인자를 가져와서 동작을 지정할 수 있다.

 

✅ doAnswer에서 mock 객체의 상태를 변경 시 실제 mock 객체에 반영된다.

✅ return null : void 함수이므로 리턴값은 null로 설정한다.

 

공식문서 link

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#do_family_methods_stubs

 

Mockito - mockito-core 5.11.0 javadoc

Latest version of org.mockito:mockito-core https://javadoc.io/doc/org.mockito/mockito-core Current version 5.11.0 https://javadoc.io/doc/org.mockito/mockito-core/5.11.0 package-list path (used for javadoc generation -link option) https://javadoc.io/doc/org

javadoc.io

 

doAnswer 사용 예시

doAnswer는 특정 인자들에 대해서만 동작을 수행하도록 하여 결과값이 예상대로 잘 나오는지 검증할 수 있다.

 

외부에서 값을 넣어줄 수 없을때 동작을 지정하여 mocking할 수 있다.

 

특별 주문

특별 주문은 하루에 발생한 총 주문 중 하나를 뽑아서 특별 사인을 함께 주는 서비스이다.

public class OrderService {
    private ShippingService shippingService;

    public OrderService(ShippingService shippingService) {
        this.shippingService = shippingService;
    }

    public void specialOrders(List<Order> orders) {
        for (Order order : orders) {
            if (order.getStatus() == OrderStatus.PENDING) {
                shippingService.specialOrder(order);
            }
        }
    }
}

 

specialOrder에 들어간 order 인자들 중 하나를 무작위로 뽑아서 특별 주문으로 선택한다.

 

만약 테스트에서 내가 원하는 주문의 id로 특별 주문을 설정하고 잘 돌아가는지 검증(assert)하고 싶다면?

doAnswer를 사용하자.

 

import static org.mockito.Mockito.*;

public class OrderServiceTest {
    private ShippingService shippingService;
    private OrderService orderService;

    @BeforeEach
    void setUp() {
        shippingService = mock(ShippingService.class);
        orderService = new OrderService(shippingService);
    }

    @Test
    void testProcessOrders() {
        // given
        Order order1 = new Order(1L, OrderStatus.PENDING);
        Order order2 = new Order(2L, OrderStatus.PENDING);
        List<Order> orders = Arrays.asList(order1, order2);

        doAnswer(invocation -> {
            Order order = invocation.getArgument(0);
            if (order.getId() == 1L) {
                order.setStatus(OrderStatus.SPECIAL);
            }
            return null;
        }).when(shippingService).specialOrder(any(Order.class));

        // when
        orderService.processOrders(orders);

        // then
        verify(shippingService, times(1)).specialOrder(any(Order.class));
        assertEquals(OrderStatus.SPECIAL, order1.getStatus());
        assertEquals(OrderStatus.SHIPPED, order2.getStatus());
    }
}

1번 주문이 특별 주문으로 설정되게 하여 결과를 만들어 내고 then에서 검증할 수 있다.

 

즉, doAnswer는 특정 인자들에 대해서만 동작을 수행하도록 하여 결과값이 예상대로 잘 나오는지 검증할 수 있다.

 
728x90