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)
// 개발 클래스
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);
}
}
// 개발 클래스
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을 배우며 삽질한 나의 경험