[Test] void 메서드 테스트 및 Stubbing (모의 동작 지정하기)
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
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는 특정 인자들에 대해서만 동작을 수행하도록 하여 결과값이 예상대로 잘 나오는지 검증할 수 있다.