[JUnit] default constructor, private method 호출하기

HU·2022년 3월 17일
0

JUnit

목록 보기
1/1

JUnit에서 단위 및 통합 테스트를 하려는데 난간에 부딪혔다
1. 생성자의 접근제어자가 default 였던 것
2. 단위 테스트할 메소드의 접근제어자가 private 였던 것

해결 방법을 기록해두자.


가짜 객체를 만들어보자

이로 인해 test 패키지에서 new를 통해 해당 클래스에 접근이 불가능했다.
그래서 사용한 것이 Mock을 이용한 가짜 객체를 생성하는 방법이었다.

먼저 호출할 메소드가 있는 클래스를 mock 처리 한다.

// 개발 클래스
public class ExampleClass {
	ExapmpleClass(){
    	new ExampleObject();
	}
    
    private String exampleMethod(String parameter) {
    	...
        return "반환";
    {
}

// JUnit 클래스
class exampleClass {
	// 1. mock 이용
	// ExampleClass exampleClass = mock(ExampleClass.class);
    
    // 2. spy 이용
    ExampleClass exampleClass = spy(ExampleClass.class);   
}

mock VS spy
mock: 껍데기만 가져온다.
spy: 실제 객체를 모방한다.

나는 생성자에서 다른 객체를 생성해야므로 껍데기만 있어서는 안 되어 spy를 사용했다.



private 메소드를 호출해보자

내가 알고있는 private에 접근하는 방법은 2가지다.
1. reflection
2. Mock(PowerMock)

reflection 사용

// 개발 클래스
public class ExampleClass {
	ExapmpleClass(){
    	new ExampleObject();
	}
    
    private String exampleMethod(String parameter) {
    	...
        return "리플렉션";
    {
}


// JUnit 클래스
class exampleClass {
	
    Class<?> clazz = ExampleClass.class;
    
    // 2. spy 이용
    ExampleClass exampleClass = spy(ExampleClass.class);   

	@Test
    void 리플렉션_사용(){
    	// reflection
        Method method = this.clazz.getDeclaredMethod("exampleMethod");
        
        // given
        String expectValue = "리플렉션";
        
        // when
        // default 생성자는 spy 때문에 자동으로 호출된다.        
        method.setAccessible(true);
        String resultValue = (String) method.invoke(this.exampleClass);
        
        // then
        assertThat("파워모키토").isNotEqualsTo(resultValue);
        assertThat(expectValue).isEqualsTo(resultValue);
    }

}

PowerMockito 사용

// 개발 클래스
public class ExampleClass {
	ExapmpleClass(){
    	new ExampleObject();
	}
    
    private String exampleMethod(String parameter) {
    	...
        return "파워모키토";
    {
}


// JUnit 클래스
class exampleClass {
	
    // 2. spy 이용
    ExampleClass exampleClass = spy(ExampleClass.class);   

	@Test
    void 파워목_사용(){
    	
        // given
        String givenParameter = "파워목";
        String expectValue = "예상 값";
        
        // when
        // thenReturn() : 반환 값을 지정한 값으로 넘겨준다.
        PowerMockito.when(this.exampleClass, "exampleMethod", givenParameter)
                    .thenReturn("예상 값");
        
        // invokeMethod(목 클래스, "메소드 명", 매개변수{여러 개일 경우 여러 개 입력})
        // 매개변수를 한정 짓고 싶지 않으면 parameter 대신 ArgumentMatchers.anyString() 을 대신 작성하면 된다.
        String resultValue = Whitebox.invokeMethod(this.exampleClass, "exampleMethod", parameter);
        
        // then
        assertThat(expectValue).isEqualsTo(resultValue);
    }
    
}

spy(클래스.class) -> PowerMock.spy(생성자.newInstance());

( PowerMock의 spy로 변경하는 방법 )

저런. 작성하고 다시 보니, this.exampleClass는 PowerMockito를 이용한 spy가 아니라, 그냥 Mockito의 spy였다.

class exampleClass {
	ExampleClass exampleClass =	PowerMockito.spy(ExampleClass.class);
    
    @Test
    void exampleMethod () {
    	...
    }
}

라고 같은 느낌으로 변경하려니
"Type mismatch: cannot convert from void to ExampleClass"
-> default 생성자가 void 타입이라 ExampleClass으로 바꿀 수 없다고 한다.

해결 방법

class exampleClass {
	Class<?>           clazz                = ExampleClass.class;
    Constructor<?>     constructor          = null;
    
    ExampleClass       exampleClass         = null;

    @BeforeEach 
    void PowerMock.spying_기본_생성자() throws Exception {
        // given & reflection
        this.constructor = this.clazz.getDeclaredConstructor();
        this.constructor.setAccessible(true);

        // when
        this.exampleClass = (ExampleClass) PowerMockito.spy(this.constructor.newInstance());

        // then
        assertThat(this.exampleClass).isNotNull();
    }
}

given 주석처럼 세팅을 해줘야 when 주석에서 PowerMOickito.spy를 할 수 있으므로, 다른 Test 메소드 실행 전 이 부분을 동작시키기 위해 BeforeEach를 했다.



출처

(PowerMock.spy 코드) How to use Spy class in PowerMockito for Final class which has private Consturctor ?

+ JUnit, Mock, PowerMock을 배우며 삽질한 나의 경험

profile
지식 쌓기

0개의 댓글