[Java/JUnit] Reflection을 활용한 private 메서드 테스트 방법

최지수·2022년 4월 22일
0

Java

목록 보기
18/27
post-thumbnail

private 메서드를 테스트하고 싶어요

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);

실제로 코드를 사용할 때에는 getDeclaredMethodNoSuchMethodException, 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를 넣고, invokeObject[]를 넣어주면 되요.

// 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

profile
#행복 #도전 #지속성

0개의 댓글