Test 코드를 작성하다보면 private method를 테스트해야할 경우가 생긴다.
이러한 경우 Reflection을 사용하면 테스트가 가능하다.
여기서는 Spring의 ReflectionTestUtils와 Java의 Reflection을 사용하는 방법에 대해 작성하였다.
data class Human(
val age: Int
) {
private fun isAdult(): Boolean {
return age > 19
}
}
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.springframework.test.util.ReflectionTestUtils
import java.lang.reflect.Method
class HumanTest {
@Test
fun springTest() {
val adult = Human(20)
val child = Human(10)
assertTrue(ReflectionTestUtils.invokeMethod<Boolean>(adult, "isAdult")!!)
assertFalse(ReflectionTestUtils.invokeMethod<Boolean>(child, "isAdult")!!)
}
@Test
fun test() {
val adult = Human(20)
val child = Human(10)
val method: Method = Human::class.java.getDeclaredMethod("isAdult")
method.setAccessible(true)
val adultResult = method.invoke(adult) as Boolean
val childResult = method.invoke(child) as Boolean
assertTrue(adultResult)
assertFalse(childResult)
}
}
springTest()
는 Spring의 ReflectionTestUtils를 사용한 방법이다.
아래의 메서드가 ReflectionTestUtils의 invokeMethod 메서드이다.
targetObject 혹은 targetClass를 넘겨주고, 메서드 이름과 인자를 넘겨주면 해당 메서드를 실행시켜주는 방식으로 구현되어있다.
public class ReflectionTestUtils {
@Nullable
public static <T> T invokeMethod(Class<?> targetClass, String name, Object... args) {
Assert.notNull(targetClass, "Target class must not be null");
return invokeMethod(null, targetClass, name, args);
}
@Nullable
public static <T> T invokeMethod(@Nullable Object targetObject, @Nullable Class<?> targetClass, String name,
Object... args) {
Assert.isTrue(targetObject != null || targetClass != null,
"Either 'targetObject' or 'targetClass' for the method must be specified");
Assert.hasText(name, "Method name must not be empty");
try {
MethodInvoker methodInvoker = new MethodInvoker();
methodInvoker.setTargetObject(targetObject);
if (targetClass != null) {
methodInvoker.setTargetClass(targetClass);
}
methodInvoker.setTargetMethod(name);
methodInvoker.setArguments(args);
methodInvoker.prepare();
if (logger.isDebugEnabled()) {
logger.debug(String.format("Invoking method '%s' on %s or %s with arguments %s", name,
safeToString(targetObject), safeToString(targetClass), ObjectUtils.nullSafeToString(args)));
}
return (T) methodInvoker.invoke();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
throw new IllegalStateException("Should never get here");
}
}
}
test()
는 java의 Reflection을 사용한 방법이다.
public class Class {
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name);
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
return getReflectionFactory().copyMethod(method);
}
}
이름을 통해 메서드를 찾은 다음 복사하여 메서드를 반환하는 방식으로 구현되어있다.
이후 테스트 코드에서 method.setAccessible(true)
접근이 가능하도록 수정한 후 private 메서드를 동작시킨다.
| Ref. |
'IT > Spring' 카테고리의 다른 글
AOP 사용하여 로깅 (0) | 2023.09.25 |
---|---|
Interceptor를 사용하여 Request/Reponse Logging (0) | 2023.09.20 |
Annotation Bean register (0) | 2023.09.07 |
security 6.1 마이그레이션 (1) | 2023.05.19 |
Base64Utils Deprecated (0) | 2023.02.28 |
댓글