728x90
public class ConsoleInput implements Input {
private InputValidator inputValidator;
public ConsoleInput() {
this.inputValidator = new InputValidator();
}
...
}
이런식으로 클래스 내부에서 속성을 만들어 사용하는 경우에 InputValidator에 대한 mock객체를 어떻게 넣어야하나 궁금했다. 그래서 구글링을 해봤다
https://cornswrold.tistory.com/369
속성에 mock객체를 주입하는 방법에 InjectMock이라는 어노테이션을 사용할 수 있었다.
@Mock
MemberDao memberDao;
@InjectMocks
MemberService memberService;
@Test
void test() {
when(memberDao.getMemberCount()).thenReturn(0);
Member member = new Member();
assertThat(memberService.createMember(), is(member));
}
- 예시를 보면 이런식이었다. 그래서 내 코드에도 적용을 시켜봤다
class ConsoleInputTest {
@Mock
static InputValidator inputValidator = mock(InputValidator.class);
@InjectMocks
static ConsoleInput consoleInput = new ConsoleInput();
@BeforeAll
static void setMock() {
when(inputValidator.validateCarNames(ArgumentMatchers.anyString()))
.thenReturn(true);
}
@Test
void test() { ... }
}
- ConsoleInput이 InputValidator를 속성으로 가지고 있고, ConsoleInput을 생성하면 내부에서 자체적으로 InputValidator를 만드는 구조이다
- inputValidator를 @Mock으로 설정하고, ConsoleInput에 @InjectMock으로 mock을 대입해줬다
- 그리고 mock에 대한 동작을 설정했다
하지만 mock객체가 제대로 대입되지 않았다. 그래서 구글링을 해보니 다음과 같이 나왔다
예, @InjectMocks 주석을 사용하면 Mockito가 생성자 주입 또는 setter/필드 주입을 수행하지만 둘 다 수행하지는 않습니다. 선택될 규칙은 상당히 복잡합니다. 이것이 제가 가능할 때마다 @InjectMocks를 사용하지 않으려고 노력하는 이유 중 하나입니다. 요약하자면, Mockito FIRST는 클래스에 있는 생성자 중에서 하나를 선택한 다음 해당 생성자가 생성자 주입에 사용될 수 있는지 분석합니다. 그것이 선택하는 것은 항상 가장 많은 인수를 가진 것입니다. 동일한 수의 인수를 가진 생성자가 여러 개 있는 경우 어느 것이 선택될지는 정의되지 않습니다. CHOSEN 생성자의 매개변수 중 하나 이상의 유형이 기본 유형이거나 최종 클래스 또는 개인 클래스인 경우 생성자 주입은 사용되지 않습니다. 사용할 수 있는 다른 생성자가 있더라도 마찬가지입니다. 생성자 주입이 사용되지 않거나 유일한 생성자가 기본 생성자인 경우 대신 setter/필드 주입이 사용됩니다. 그러나 setter/필드 주입은 생성자 주입과 함께 사용되지 않습니다.
요약하면, @InjectMock을 수행하면 클래스에 있는 생성자를 찾아 주입이 가능한지 확인한다. 그리고 마땅한 생성자가 없다면 setter/필드 주입을 사용한다고 한다.
즉, 내 코드처럼 아예 막혀있는 경우라면 mock이 대입이 되지 않는다고 한다...!!!!
@InjectMock을 타고들어가 설명을 읽어봐도 다음과 같이 나온다
주입을 수행해야 하는 필드를 표시합니다.
- shorthand mock 및 spy 주입을 허용합니다.
- 반복적인 mock 및 spy 주입을 최소화합니다.
Mockito는 아래에 설명된 대로 생성자 주입, 속성 주입 또는 설정자 주입을 통해서만 mock 주입을 시도합니다. 다음 전략 중 하나라도 실패하면 Mockito는 실패를 보고하지 않습니다. 즉, 종속성을 직접 제공해야 합니다.
1. 생성자 주입; 가장 큰 생성자가 선택된 다음 인수는 테스트에서만 선언된 mock 객체로 해결됩니다. 생성자를 사용하여 객체가 성공적으로 생성되면 Mockito는 다른 전략을 시도하지 않습니다. Mockito는 매개변수가 있는 생성자가 있는 경우 객체를 손상시키지 않기로 결정했습니다.
참고: 인수를 찾을 수 없으면 null이 전달됩니다. 조롱할 수 없는 유형이 필요한 경우 생성자 주입이 발생하지 않습니다. 이러한 경우 종속성을 직접 충족해야 합니다.
2. 적절한 setter 주입; mock 객체는 먼저 유형별로 해결됩니다(이름에 관계없이 단일 유형 일치 주입이 발생하는 경우). 그런 다음 동일한 유형의 여러 속성이 있는 경우 속성 이름과 mock 이름의 일치로 해결됩니다.
참고 1: 동일한 유형(또는 동일한 삭제)의 속성이 있는 경우 일치하는 속성으로 @Mock 주석이 달린 모든 필드의 이름을 지정하는 것이 좋습니다. 그렇지 않으면 Mockito가 혼동될 수 있으며 주입이 발생하지 않습니다.
참고 2: @InjectMocks 인스턴스가 이전에 초기화되지 않았고 인수가 없는 생성자가 있는 경우 이 생성자를 사용하여 초기화됩니다.
3. field 주입; mock 객체는 먼저 유형별로 해결됩니다(이름에 관계없이 단일 유형 일치 주입이 발생하는 경우). 그런 다음 동일한 유형의 여러 속성이 있는 경우 필드 이름과 mock 이름의 일치로 해결됩니다.
참고 1: 동일한 유형(또는 동일한 삭제)의 필드가 있는 경우 일치하는 필드로 @Mock 주석이 달린 모든 필드의 이름을 지정하는 것이 좋습니다. 그렇지 않으면 Mockito가 혼란스러워서 주입이 발생하지 않을 수 있습니다.
참고 2: @InjectMocks 인스턴스가 이전에 초기화되지 않았고 인수가 없는 생성자가 있는 경우 이 생성자를 사용하여 초기화됩니다.
결론은 생성자, setter, 필드주입 모두 막혀있는 경우에는 클래스 속성에 mock을 넣어 테스트할 방법이 없다!
mock을 주입하려면 생성자를 열거나, setter, 필드주입을 허용하는 방법밖에 없는 것 같다.
(저는 알맞은 생성자를 만드는 방법을 택했습니다)
728x90
'프로그래밍 언어 > Java' 카테고리의 다른 글
[JAVA] 상수를 private static final로 선언하는 이유 (0) | 2023.11.07 |
---|---|
[JAVA] 단위테스트 공부3: 활용하기 (0) | 2023.11.01 |
[우테코] 2주차 Test코드 분석 - NsTest, assertj, JUnit (0) | 2023.10.30 |
[JAVA] 문자열 양쪽 공백을 제거하는 trim과 strip 비교 (0) | 2023.10.29 |
[JAVA] 단위테스트 공부2: 테코톡 보고 정리 (0) | 2023.10.29 |