테스트를 작성할 때 정적 메서드를 모의해야 하는 상황에 자주 직면하게 됩니다. Mockito 버전 3.4.0 이전에는 정적 메서드를 직접 모의하는 것이 불가능했습니다. PowerMockito의 도움을 통해서만 가능했습니다.
이 튜토리얼에서는 최신 버전의 Mockito를 사용하여 정적 메서드를 모의하는 방법을 살펴보겠습니다.
Mockito를 사용한 테스트에 대해 자세히 알아보려면 포괄적인 Mockito 시리즈를 확인하세요.
테스트의 초점은 간단한 정적 유틸리티 클래스입니다.
public class StaticUtils {
private StaticUtils() {}
public static List<Integer> range(int start, int end) {
return IntStream.range(start, end)
.boxed()
.collect(Collectors.toList());
}
public static String name() {
return "Baeldung";
}
}
데모 목적으로 몇 가지 인수가 포함된 메서드 하나와 단순히 String을 반환하는 메서드가 있습니다.
일반적으로 어떤 사람들은 깔끔한 객체 지향 코드를 작성할 때 정적 클래스를 모의할 필요가 없다고 말할 수도 있습니다. 이는 일반적으로 애플리케이션의 디자인 문제나 코드 냄새를 암시할 수 있습니다.
왜? 첫째, 정적 메서드에 의존하는 클래스는 긴밀한 결합을 가지며, 둘째, 거의 항상 테스트하기 어려운 코드로 이어집니다. 이상적으로는 클래스가 종속성을 획득하는 일을 담당해서는 안 되며, 가능하다면 외부에서 주입해야 합니다.
따라서 코드를 더 테스트하기 쉽게 리팩터링할 수 있는지 항상 조사해 볼 가치가 있습니다. 물론 이것이 항상 가능한 것은 아니며 때로는 정적 메서드를 모의해야 할 때도 있습니다.
StaticUtils 클래스에서 name 메소드를 모의하는 방법을 살펴보겠습니다.
@Test
void givenStaticMethodWithNoArgs_whenMocked_thenReturnsMockSuccessfully() {
assertThat(StaticUtils.name()).isEqualTo("Baeldung");
try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {
utilities.when(StaticUtils::name).thenReturn("Eugen");
assertThat(StaticUtils.name()).isEqualTo("Eugen");
}
assertThat(StaticUtils.name()).isEqualTo("Baeldung");
}
이전에 언급했듯이 Mockito 3.4.0부터 Mockito.mockStatic(Class<T> classToMock) 메서드를 사용하여 정적 메서드 호출에 대한 호출을 모의할 수 있습니다. 이 메소드는 범위가 지정된 모의 객체인 우리 유형에 대한 MockedStatic 객체를 반환합니다.
따라서 위의 단위 테스트에서 유틸리티 변수는 스레드 로컬 명시적 범위가 있는 모의 변수를 나타냅니다. 범위가 지정된 모의는 모의를 활성화하는 엔터티에 의해 닫혀야 한다는 점에 유의하는 것이 중요합니다. 이것이 우리가 범위가 지정된 블록을 완료할 때 모의가 자동으로 닫히도록 try-with-resources 구성 내에서 모의를 정의하는 이유입니다.
이는 정적 모의 객체가 일시적으로 유지되도록 보장하므로 특히 좋은 기능입니다. 아시다시피, 테스트 실행 중에 정적 메서드 호출을 가지고 놀면 테스트 실행의 동시 및 순차적 특성으로 인해 테스트 결과에 부정적인 영향을 미칠 수 있습니다.
게다가, 또 다른 좋은 부작용은 Mockito가 모든 테스트에 대해 클래스 로더를 교체할 필요가 없기 때문에 테스트가 여전히 매우 빠르게 실행된다는 것입니다.
예제에서는 범위가 지정된 블록 전후에 정적 메서드 name이 실제 값을 반환하는지 확인하여 이 점을 반복합니다.
이제 인수가 있는 메서드를 모의해야 하는 또 다른 일반적인 사용 사례를 살펴보겠습니다.
@Test
void givenStaticMethodWithArgs_whenMocked_thenReturnsMockSuccessfully() {
assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);
try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {
utilities.when(() -> StaticUtils.range(2, 6))
.thenReturn(Arrays.asList(10, 11, 12));
assertThat(StaticUtils.range(2, 6)).containsExactly(10, 11, 12);
}
assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);
}
여기서는 동일한 접근 방식을 따르지만 이번에는 조롱하려는 인수와 함께 메서드를 지정하는 when 절 내부에 람다 식을 사용합니다. 꽤 직설적 인!