1. 문제 정의
Java 애플리케이션에서 private 메서드, 필드 또는 내부 클래스를 테스트해야 할 때, 이들이 외부에서 접근할 수 없다는 이유로 테스트가 어려울 수 있습니다. 하지만 코드의 안정성을 보장하기 위해 내부 로직도 테스트해야 할 때가 있습니다.
이 문서에서는 JUnit을 사용하여 private 메서드 및 필드를 테스트하는 다양한 방법과 최적의 접근 방식을 설명합니다.
2. 왜 private 메서드를 직접 테스트해야 하나요?
2.1 일반적인 권장 사항
- Public 메서드를 테스트하세요:
일반적으로 private 메서드는 public 메서드를 통해 간접적으로 테스트해야 합니다. - 예: public 메서드가 내부적으로 private 메서드를 호출할 때, 자연스럽게 테스트됩니다.
2.2 테스트가 필요한 상황
- 복잡한 로직을 포함한 private 메서드가 있는 경우.
- 내부적으로 사용되는 메서드의 독립적인 검증이 필요한 경우.
- 유지보수와 디버깅의 용이성을 위해 private 메서드의 개별 테스트가 필요할 때.
3. 테스트 방법
3.1 Reflection을 사용한 테스트
Java의 Reflection API를 사용하면 private 메서드 및 필드에 접근할 수 있습니다. Reflection은 런타임에 클래스 정보를 읽고 수정할 수 있는 강력한 도구입니다.
예제: Reflection을 사용해 private 메서드 테스트
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ReflectionTest {
@Test
void testPrivateMethod() throws Exception {
// 테스트 대상 클래스의 인스턴스 생성
MyClass obj = new MyClass();
// private 메서드 호출
Method method = MyClass.class.getDeclaredMethod("privateMethod", int.class);
method.setAccessible(true); // private 메서드 접근 허용
// 메서드 호출 및 결과 검증
int result = (int) method.invoke(obj, 5);
assertEquals(25, result); // 기대 값과 비교
}
}
class MyClass {
private int privateMethod(int num) {
return num * num;
}
}
장점
- 기존 코드 수정 없이 private 메서드 테스트 가능.
단점
- Reflection 사용은 성능에 영향을 줄 수 있으며, 코드 가독성을 저하시킬 수 있음.
3.2 설계를 개선하여 테스트
가능한 경우 private 메서드의 설계를 개선하여 테스트를 간접적으로 수행할 수 있습니다.
1. 리팩토링으로 private 메서드 제거
private 메서드의 로직이 중요한 경우, 해당 로직을 별도의 클래스로 분리하여 독립적으로 테스트할 수 있습니다.
class MathUtility {
public int square(int num) {
return num * num;
}
}
class MyClass {
private MathUtility utility = new MathUtility();
public int calculateSquare(int num) {
return utility.square(num);
}
}
2. Protected 접근자로 변경
테스트 목적이라면 private 대신 protected
로 변경하고, 동일 패키지 내 테스트 클래스를 작성할 수 있습니다.
3.3 Mockito를 사용한 테스트
Mockito와 같은 Mocking 프레임워크를 사용하여 private 메서드를 호출하는 public 메서드를 테스트할 수 있습니다.
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
class MyClass {
private int privateMethod(int num) {
return num * num;
}
public int calculate(int num) {
return privateMethod(num) + 10;
}
}
public class MockitoTest {
@Test
void testPublicMethodWithPrivateLogic() {
MyClass obj = spy(MyClass.class);
// private 메서드 모킹
doReturn(25).when(obj).privateMethod(5);
// 결과 검증
assertEquals(35, obj.calculate(5));
}
}
4. 결론
Java에서 private 메서드를 테스트하는 것은 권장되지는 않지만, Reflection, 리팩토링, 또는 Mocking 프레임워크를 사용하면 가능해집니다. 가장 중요한 것은 테스트를 통해 클래스의 계약(Contract)을 검증하며, 필요에 따라 적절한 접근 방법을 선택해야 합니다.