JUnit
으로 테스트하다 보면 클래스, 메서드 또는 모듈 단위로 테스트를 진행해요. 문제는 클래스 내 메서드를 private
로 캡슐화Encapsulation
로 인해 내부 메서드를 테스트를 하는데 어려움이 있어요. 그렇다고 어렵사리 캡슐화된 메서드를 테스트할 때 쓴답시고 public
선언하는건 매우 비효율적이에요.
그래서 이를 테스트하기 위해 자바Java
의 고유 기능인 reflection
을 통해 테스트를 수행해요.
public class Add {
private int add(int a, int b) {
return a + b;
}
}
클래스에 위와 같은 메서드가 있다고 가정해봐요. 이걸 JUnit
테스트 코드에선 아래와 같이 선언해서 테스트할 수 있어요.
import org.junit.Test;
import static org.junit.Assert.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public Test {
@Test
public void test() {
Method method = Add.class.getDeclaredMethod("add", int.class, int.class);
method.setAccessible(true);
int ret = method.invoke(1, 2);
assertEquals(3, ret);
}
}
하나씩 알아보도록 해요 ㅎㅎ. 우선 사용하려는 private
함수를 가져오기 위해 Method
변수를 선언해요. 이 때 매개변수s
는 호출하려는 메서드의 매개변수에 타입에 맞춰 기입하면 되요.
Method method = {클래스 명}.class.getDeclaredMethod("함수 명", {매개변수s});
이렇게 Method
인스턴스에 메서드를 담았어요. 문제는 이러고 끝이 아니라, private
메서드를 접근 가능하게 하기 위해 setAccessible(true)
도 선언해줘야 해요.
method.setAccessible(true);
마지막으로 invoke
를 통해 메서드를 실행시켜주면 되요. 매개변수도 당연히 적절하게 넣어주셔야 해요!
Add add = new Add(); // 참조 변수 선언
int ret = (int)method.invoke(add, 1, 2); // 매개변수 적절히 추가
assertEqual(3, ret);
실제로 코드를 사용할 때에는 getDeclaredMethod
가 NoSuchMethodException
, SecurityException(Runtime)
, IllegalAccessException
예외를 던지고, invoke
함수가 IllegalAccessException
, IllegalArgumentException(Runtime)
, InvocationTargetException
예외를 던지니까 적절한 try-catch
문이 필요해요.
코드를 보시면 어떻게 짜시는지 이해하실 수 있을 거에요.
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class AppTest {
@Test
public void test() {
try {
Method method = InitCommand.class.getDeclaredMethod("add", int.class, int.class);
method.setAccessible(true);
assertEquals(3, (boolean) method.invoke(new Add, 1, 2));
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
// RuntimeException은 필수가 아니에요!
// catch (SecurityException e) {
// e.printStackTrace();
// }
catch (IllegalAccessException e){
e.printStackTrace();
}
// RuntimeException은 필수가 아니에요!
// catch (IllegalArgumentException e) {
// e.printStackTrace();
// }
catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
invoke
하려는 메서드의 매개변수가 가변인자인 경우는 조금 복잡해요. 이 땐 getDeclaredMethod
에 매개변수를 정의하는 곳에 {타입}[].class
를 넣고, invoke
에 Object[]
를 넣어주면 되요.
// add 메서드가 다수의 정수를 더하는 함수가 되었다 가정해요.
Method method = InitCommand.class.getDeclaredMethod("add", int[].class);
method.setAccessible(true);
assertEquals(10, (int) method.invoke(new InitCommand(), new Object[]{ 1, 2, 3, 4}));
crocus님 블로그
Stack Overflow - How to work with varargs and reflection