본문 바로가기

TEST CODE

[JUnit]같은 타입인 여러개의 Mock 객체를 @InjectMock 으로 주입 시 파라미터를 제대로 인식하지 못하는 문제 해결

 

 

Reflection 을 이용하여 2개의 Mapper동적으로 호출하기 위한 Util 을 만들기 위해 다음과 같이 Object 타입의 클래스를 2개 주입받아 초기화되는 로직을 작성하였습니다.

    private Object firstMapper;
    private Object secondMapper;

    public ReflectionUtils(Object firstMapper, Object secondMapper) {
        this.firstMapper = firstMapper;
        this.secondMapper = secondMapper;
    }

 

이를 테스트 하기 위해 다음과 같이 Mapper 를 @Mock 으로 생성하여 @InjectMock 으로 주입하고자 했습니다.

@RunWith(MockitoJUnitRunner.class)
public class ReflectionUtilsTest {

    @Mock
    private FirstMapper firstMapper;

    @Mock
    private SecondMapper secondMapper;

    @InjectMocks
    private ReflectionUtils reflectionUtils;

    ...
}

 

하지만 다음과 같이 작업 후 테스트 메서드를 실행하였더니, FirstMapper 와 SecondMapper 를 구분하지 못하는 문제가 발생하였습니다.

    @Test
    public void first_를_인자로_넘겼을때_firstMapper_를_리턴하는지() {
        //given
        String service = ServiceGroup.FIRST.getServiceName();

        //when
        Class<?> className = reflectionUtils.getMapper(service);

        //then
        assertThat(className).isEqualTo(firstMapper.getClass());
    }

 

이러한 문제가 발생하는 이유는 javadoc 에서 다음과 같은 설명이 나와있습니다.

https://javadoc.io/static/org.mockito/mockito-core/3.1.0/org/mockito/InjectMocks.html

 

Field injection; mocks will first be resolved by type (if a single type match injection will happen regardless of the name), then, if there is several property of the same type, by the match of the field name and the mock name.

Note 1: If you have fields with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen.

 

위의 내용을 보면 다음과 같은 내용을 볼 수 있습니다.

mocks will first be resolved by type (if a single type match injection will happen regardless of the name), then, if there is several property of the same type, by the match of the field name and the mock name.

-> mock들을 우선 타입으로 구분하여 처리하며 만약 같은 타입이 있는 경우에는 field name 과 mock name 을 매칭하여 처리합니다.

Note 1: If you have fields with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen.

-> 같은 타입의 경우에는 이름을 모두 지정해주는 것을 선호하고 있습니다. 만약 지정하지 않는다면, 혼란이 발생해 injection 이 발생하지 않을 수 있습니다.

 

이처럼 InjectMocks 를 사용할 때는 초기화 시 사용했던 객체의 선언 명을 Mock 객체에 name 을 지정해줘야 합니다.

    @Mock(name = "firstMapper")
    private FirstMapper firstMapper;

    @Mock(name = "secondMapper")
    private SecondMapper secondMapper;

 

만약, 다음과 같이 지정해줘도 혼란을 발생하는 경우에는 @injectMocks 를 제거하고 다음과 같이 직접 명시하여 객체를 초기화하여 생성해줘야 합니다.

    @Before
    public void setUp() {
        reflectionUtils = new ReflectionUtils(firstMapper, secondMapper);
    }

그럼 문제 없이 Mock 객체가 주입되어 실행되어 테스트가 통과하는 것을 볼 수 있습니다.

만약 같은 타입의 객체를 주입하는 경우에는 @InjectMocks 를 사용하지 않고 직접 초기화해주는 것이 혼란을 야기하지 않을 수 있습니다.