본문 바로가기
개발/기타

Mockito verify()에서 파라미터 까지 검증하고 싶은 경우

by hamcheeseburger 2024. 5. 29.

Junit5 + Mockito를 함께 사용하는 예제를 준비했습니다.

배경

Mockito 사용 시 특정 메서드가 수행됐는지 확인하기 위해 verify()를 사용하는 경우가 많다.

보통 이런 식으로 많이 사용할 것이다.

verify(mock).doSomething(any(Person.class));

 

이 때 메서드에 전달되는 파라미터 값까지 동일한지 확인하고 싶다면 어떤 방법이 있을까?

(any()로 검증하는건 타입만 확인하는 것이라 불안하다.)

ArgumentMatcher로 검증하기

ArgumentMatcher 를 implements 혹은 익명 객체로 생성하여 파라미터를 검증할 수 있다.

verify(list, times(2)).add(argThat(new ArgumentMatcher<String>(){
     public boolean matches(String arg) {
         return arg.length() < 5;
     }
 }));

 

간단하게 람다로 사용할 수도 있다.

verify(list, times(2)).add(argThat(string -> string.length() < 5));

 

ArgumentCaptor로 검증하기 (Since 1.8.0)

이름에서도 유추할 수 있듯이 인수를 캡쳐하는 역할을 한다.

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());

verify 하려는 메서드의 인수들을 캡쳐하고 assert 문에서 검증할 수 있다.

 

ArgumentCaptor를 간단히 초기화하고 싶다면 '@Captor' 를 사용할 수도 있다. (Since 1.8.3)

@ExtendWith(MockitoExtension.class)
public class TestClass {
	@Captor
	private ArgumentCaptor<Person> captor;
}

주의할점

1.  verify() 내에서는 capture() 메서드를 사용해야 한다. 해당 인수를 캡쳐 후 assert문에서 getValue()로 검증해야 한다. 

2. stubbing에 같이 사용하지 말 것.

- captor가 then(결과 확인) 블록 외부에 생성되므로 테스트 가독성이 저하될 수 있음.

- stubbing 메서드가 호출되지 않은 경우에는 인수가 캡쳐되지 않으므로 결함 발견이 힘들 수 있음.

- stubbing에는 ArgumentMatcher를 사용하는 것이 더 적합하다.

 

ArgumentMatcher보다 ArgumentCaptor가 적합한 경우

  1. 생성한 ArgumentMatcher가 재사용되지 않는 경우 → ArgumentMatcher를 람다로 대체하면 재사용되지 않는 문제는 해결될 것 같다.
  2. 검증을 완료하기 위해 argument value를 assert 내에 포함해야하는 경우
  3. (개인적 의견) 검증해야할 파라미터가 주소 값이 다른 객체인 경우
    - ArgumentMatcher를 사용하면 matches() 구현 아래 객체 내 필드들이 동등한지 비교하는 로직이 들어가야 하는데, ArgumentCaptor를 사용하면 usingRecursiveComparision()를 사용하여 쉽게 동등성을 확인할 수 있을 것 같다.
    - 객체 내에 equals&hashCode를 재정의하면 ArgumentMatcher로도 쉽게 사용할 수 있을테지만, 테스트에서만 사용하는 코드라면 재정의하는 것이 부적합할 수 있다.

ArgumentMatcher 사용

verify(list, times(2)).add(argThat(person -> {
		return person.getName.equals("name") && person.getAge(20) && ...
}));

 

ArgumentCaptor 사용

Person expected = new Person("name", 20, ...);
verify(mock).doSomething(argument.capture());
assertThat(argument.getValue()).usingRecursiveComparison().isEqualTo(expected);

Verification with assertions (Since 5.3.0)

verify 내에서 assert 문을 활용할 수 있는 방법이다.

 verify(serviceMock).doStuff(assertArg(param -> {
     assertThat(param.getField1()).isEqualTo("foo");
     assertThat(param.getField2()).isEqualTo("bar");
   }));

 

 

참고자료

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

이전 댓글