반응형

JUnit으로 유닛 테스트를 하다보면 Method 테스트, Class 단위 테스트, 혹은 Module 단위의 테스트를 하게 된다.

 

하지만 Class, Module 단위처럼 큰 범위로 가게 되면 테스트하기 조금 어렵거나 까다로운 것들이 존재하게 된다.

 

이럴때 핵심적인 요소들은 대부분 Private 키워드로 캡슐화를 해둔 싱태인데 이를 JUnit에서 호출하고자 하면 private이기 때문에 접근이 안돼 정상적인 테스트가 불가능하다.

 

그렇다고 원본 코드를 모두 public로 바꾼다?? 이는 테스트를 안하는 것 만 못하다.

 

그럼에도 불구하고 JUnit을 통해 테스트할 때 Private 메서드와 변수에 접근하는 방법이 존재한다.

 

아래 코드에서 확인해보자.

 

Private method 접근 방법

package com.example.customnotepad;

public class JUnitTestClass {
    private int add(int a, int b){
        return a + b;
    }
}

우선 add라는 메서드가 있는데 이 메서드가 private로 존재한다.

하지만 이 메서드를 테스트해야만 하는 상황이라면 아래와 같이 테스트코드를 짜자.

 

package com.example.customnotepad;

import org.junit.Test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class JUnitTest {
    @Test
    public void test(){
        JUnitTestClass jUnitTestClass = new JUnitTestClass();

        Method method = jUnitTestClass.getClass().getDeclaredMethod("add", int.class, int.class);
        method.setAccessible(true);
        
        int ret = (int) method.invoke(jUnitTestClass, 1,2);
        assertThat(30, is(ret));
    }
}

 

우선 private 메서드를 가지고 있는 클래스를 생성해준다.

JUnitTestClass jUnitTestClass = new JUnitTestClass();

 

이후 사용하고자 하는 private 메서드를 호출하기 위해 Method 클래스를 만들어준다.

(Private 메서드 가지는 클래스).getClass().getDeclaredMethod("메서드명", 파라미터1, 파라미터2);

Method method = jUnitTestClass.getClass().getDeclaredMethod("add", int.class, int.class);

 

이때 파라미터1,2는 해당 메서드에 파라미터가 존재한다면 그 타입에 맞는 파라미터.class를 해준다.

 

이제 Private 메서드를 Method 객체에 담아냈으니 접근 허용가능하도록 만들어준다.

method.setAccessible(true);

 

마지막으로 invoke를 통해 메서드를 실행해주도록 한다.

method.invoke(메서드를 가진 클래스명, 실제 파라미터1, 실제 파라미터2);

method.invoke(jUnitTestClass, 1,2);

 

아래는 try / catch까지 포함된 실제 코드이다.

package com.example.customnotepad;

import org.junit.Test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class JUnitTest {
    @Test
    public void test(){
        JUnitTestClass jUnitTestClass = new JUnitTestClass();
        try {
            Method method = jUnitTestClass.getClass().getDeclaredMethod("add", int.class, int.class);
            method.setAccessible(true);
            int ret = (int) method.invoke(jUnitTestClass, 1,2);
            assertThat(30, is(ret));
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

당연히 인자가 1,2가 들어가서 답이 3이어야하지만 30과 다르기에 테스트 에러가 났다고 뜬다.

 

 

Private variable 접근 방법

마찬가지로 변수도 쉽게 이용할 수 있다.

package com.example.customnotepad;

public class JUnitTestClass {
    private int myVariable = 100;
}

 

package com.example.customnotepad;

import org.junit.Test;

import java.lang.reflect.Field;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class JUnitTest {
    @Test
    public void test(){
        JUnitTestClass jUnitTestClass = new JUnitTestClass();
        try {
            Field field = jUnitTestClass.getClass().getDeclaredField("myVariable");
            field.setAccessible(true);

            int value = (int)field.get(jUnitTestClass);
            assertThat(value, is(100));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

이번에는 Field를 생성하고 내가 얻고자하는 변수명을 입력해준다.

Field field = jUnitTestClass.getClass().getDeclaredField("myVariable");

 

이제 해당 변수를 field에 담았으니 접근 가능하게 만든다.

field.setAccessible(true);

 

그 후 field.get(생성한 클래스명)을 통해 얻고자했던 private value를 얻어주면 된다.

int value = (int)field.get(jUnitTestClass);

 

assertThat을 통해 100인지 확인했고 당연히 100이 맞으니 테스트를 통과됐다.

반응형