TIL(Today I Learned)

[Today I Learned - 4] Mockito의 ArgumentMatchers 와InvalidUseOfMatchersException

lazy man 2024. 1. 8. 11:43

오늘 단위테스트를 작성하던 도중 TaxInvoiceReserveItemRepository(@SpyBean) 을 설정하던 중 any() 파라미터를 사용했는데 다음과 같은 예외를 발생해서 원인에 대해 확인해보려고 합니다.

// InvalidUseOfMatchersException 예외 발생
// Invalid use of argument matchers!, 3 matchers expected, 1 recorded
when(taxInvoiceReserveItemRepository.findItemsByOrderNoWithLock(
	tiReserve.getSalesOrderNo(), 
	anyList(), 
	DocType.ORDER))
.thenReturn(tiReserve.getItems());

 

ArgumentMatchers

메소드 기능에 대한 테스트를 진행할 때 고정된 인자 값으로 테스트를 진행하는 경우도 있겠지만 메서드 안정성을 위해 다양한 값의 인자로 테스트를 진행해야 합니다. ArgumentMatchers 클래스는 메소드 파라미터를 다양한 인자 값으로 설정하여 실행할 때 메소드가 정상적으로 수행되는지 확인할 수 있는 도구입니다. mockito 5.3.1 버전을 기준으로 any(), anyList(), anyString(), anyLong() 등 다양한 메서드를 지원하고 있습니다.


에러의 원인은 무엇일까?

ArgumentMatchers가 무엇인지는 대충 알았고 위와 같은 에러가 발생하는 원인은 무엇일까? 메소드 인자 중 하나라도 ArgumentMatchers 를 사용한다면 모든 인자에 대해 ArgumentMatchers를 사용해야 한다고 합니다. 그래서 값을 고정해서 메소드를 실행해야 하기 때문에 eq 를 사용하여 아래와 같이 테스트 했는데 또 다른 예외가 발생했습니다..

// 예외!! eq(null) is not allowed, Use isNull() instead
when(taxInvoiceReserveItemRepository.findItemsByOrderNoWithLock(
	eq(tiReserve.getSalesOrderNo()), 
	anyList(), 
	eq(DocType.ORDER))) // NULL로 실행됨!
.thenReturn(tiReserve.getItems());

 

추적해보니 findItemsByOrderNoWithLock 메서드를 Querydsl로 JPQL을 생성하고 있는데 조건 절에서 DocType이 null로 넘어가는 것이 문제였다. eq를 통해 값을 지정했는데 왜 null로 넘어가는 것일까..

 

문제의 원인을 찾아봤는데 eq메서드 안의 defaultValue 메서드를 보니  PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES 맵에는 커스텀한 사용자의 Enum 클래스에 대한 설정이 저장되어 있지 않기 때문인 것 같다.

 

public static <T> T eq(T value) {
    reportMatcher(new Equals(value));
    return value == null ? null : Primitives.defaultValue(value.getClass()); 
}
public static <T> T defaultValue(Class<T> primitiveOrWrapperType) {
    return PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.get(primitiveOrWrapperType);
}

 

 

그래서 아래와 같이 any(DocType.class)로 변경해보았는데 역시 PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES에 Enum 타입이 없어서 null을 리턴하게 되면서 정상적인 테스트 확인에 실패했습니다.

when(taxInvoiceReserveItemRepository.findItemsByOrderNoWithLock(
    eq(tiReserve.getSalesOrderNo()),
    anyList(),
    any(DocType.class))).thenReturn(tiReserve.getItems());

 

 

구글링을 해봤는데 깔끔한 해결 방법이 보이지 않는다.. 이런 문제는 분명 많이 발생할 것 같은데 말이지.. 리스트 설정하기 귀찮아서 anyList() 를 사용하며 발생한 문제인데 우선은 ArgumentMatchers를 빼고 테스트를 진행해야겠다.. 다음에 또 만나면 그때는 해결하도록 해봐야지..