ReflectionTestUtils
를 이용한 프라이빗 메소드 및 필드 테스팅 🤖자바 개발자라면 누구나 한 번쯤은 프라이빗 메소드나 필드를 테스트해야 할 상황에 직면할 수 있습니다. 이러한 상황이 온다면 Java의 리플렉션 API와 Spring Framework의 ReflectionTestUtils
클래스를 활용한다면 private이 붙어 있는 메소드나 필드를 테스트 할 수 있습니다.
먼저, 테스트 대상인 MessageService
클래스와 그 필드를 살펴보겠습니다:
class MessageService {
private Integer messageCount = 100;
public String createMessage(String recipient) {
return prepareMessage(recipient);
}
private String prepareMessage(String recipient) {
return "Hello, " + recipient;
}
}
이 클래스는 prepareMessage
라는 프라이빗 메소드를 가지고 있으며, messageCount
라는 프라이빗 필드를 포함하고 있습니다.
프라이빗 메소드에 접근하기 위해 Java의 리플렉션 API를 사용해보겠습니다:
@SneakyThrows
@DisplayName("ReflectionTest")
@Test
void reflectionTest() {
MessageService messageService = new MessageService();
Method privateMethod = messageService.getClass()
.getDeclaredMethod("prepareMessage", String.class);
privateMethod.setAccessible(true);
String result = (String) privateMethod.invoke(messageService, "john");
System.out.println(result); // "Hello, john"
}
getDeclaredMethod
와 setAccessible(true)
를 사용하여 프라이빗 메소드에 접근할 수 있습니다. 이 방법은 Java의 기본 기능을 사용하지만, 코드가 다소 복잡해 보일 수 있습니다.
ReflectionTestUtils
사용하기 🌸Spring에서 제공하는 ReflectionTestUtils
를 사용하면 좀 더 간결하게 프라이빗 메소드와 필드를 테스트할 수 있습니다:
@DisplayName("ReflectionTestUtilsTest")
@Test
void reflectionTestUtilsTest() {
MessageService messageService = new MessageService();
String message = ReflectionTestUtils.invokeMethod(
messageService,
"prepareMessage",
"World"
);
assertThat(message).isEqualTo("Hello, World");
Integer count = (Integer) ReflectionTestUtils.getField(messageService, "messageCount");
assertThat(count).isEqualTo(100);
ReflectionTestUtils.setField(messageService, "messageCount", 250);
Integer updatedCount = (Integer) ReflectionTestUtils.getField(messageService, "messageCount");
assertThat(updatedCount).isEqualTo(250);
}
이 코드는 ReflectionTestUtils
를 사용하여 프라이빗 메소드를 호출하고, 프라이빗 필드의 값을 가져오고 수정합니다.
ReflectionTestUtils.getField()
를 통해 해당 필드를 가져올 수 있으며 (private,public 상관 없이 가져 올 수 있습니다.)
ReflectionTestUtils.setField()
를 통해 필드의 값을 변경할 수도 있습니다.
ReflectionTestUtils
는 Spring 테스트 환경에서 매우 유용하며, 코드를 더 간결하고 읽기 쉽게 만들어 줍니다.
다른 방법중의 하나로 private을 가지는 메소드나 필드를 public으로 교체하거나 아예 분리시켜서 하나의 클래스로(접근 할 수 있는 접근자 사용) 만드는 방법도 있습니다.
🤔Java 리플렉션과 Spring의
ReflectionTestUtils
는 강력한 도구로서, 보통은 접근할 수 없는 프라이빗 메소드와 필드에 접근할 수 있게 해줍니다. 하지만 이러한 기능을 사용할 때는 주의가 필요합니다. 프라이빗 메소드와 필드는 외부에서 접근하지 못하도록 의도적으로 숨겨져 있기 때문에, 리플렉션을 사용해 접근하는 것은 클래스의 캡슐화 원칙을 위반할 수 있습니다.
또한 리플렉션의 기능은 비용이 비싸기에 남발하게 된다면 성능에 있어 좋지 않습니다.만약 private 접근자를 가지는 메소드나 필드를 테스트를 해야한다고 느껴진다면 다시 한번 해당 해당 구조가 맞는지 고민을 해봐야 합니다. 위와 같은 경우 잘 못된 구조를 가지고 있을 수 있기에 분리하는 것이 좋습니다.
따라서, 리플렉션은 테스트 목적으로만 제한적으로 사용해야 하며, 애플리케이션의 일반적인 로직에서는 피하는 것이 좋습니다.
참고자료 및 출처 : Spring Webflux 완전 정복 :
코루틴부터 리액티브 MSA 프로젝트까지