스프링 테스트 코드에서 @MockBean과 @Mock의 이해와 활용
📌 서론
테스트 코드 작성 시 자주 마주치는 @Mock과 @MockBean은 비슷해 보이지만, 각각 다른 목적과 환경에서 사용된다. 이 두 어노테이션의 차이점을 명확히 이해하고 올바르게 활용하는 것은 효율적인 테스트 코드 작성에 중요하다.
@MockBean이란 무엇인가?
@MockBean은 Spring Boot 테스트 환경에서 사용되는 어노테이션으로, 가짜(모의) 빈을 생성하여 스프링 애플리케이션 컨텍스트(ApplicationContext에)에 추가한다. Mockito 테스팅 프레임워크를 사용하여 실제 구현체를 갖지 않는 모의 객체를 생성한다. 주로 통합 테스트에서 사용된다.
@MockBean의 역할
의존성 격리
@MockBean은 실제 빈 대신 모의 객체를 주입받아, 테스트 대상 클래스가 의존하는 다른 컴포넌트의 구현에 영향을 받지 않도록 한다. 이를 통해 테스트가 특정 컴포넌트의 기능에만 집중할 수 있다.
외부 시스템과의 결합 해제
데이터베이스, 외부 API, 서비스 등 실제 외부 시스템과의 통신을 필요로 하는 빈을 테스트할 때 외부 요소의 실제 동작을 모방하는 모의 객체를 제공한다.
행동 검증
Mockito를 사용하여 모의 객체의 특정 메소드가 호출되었는지, 어떤 매개변수로 호출되었는지 등을 검증한다.
@MockBean 사용 예시
다음 코드는 @MockBean을 사용하여 MyRepository의 인스턴스를 모의 객체로 대체한다. 이 모의 객체는 스프링 컨텍스트 전체에 적용되어, 테스트 중에 MyRepository가 필요한 모든 곳에 이 모의 객체가 사용된다.
@SpringBootTest
class MyServiceTest {
@Autowired
private MyService myService;
@MockBean
private MyRepository myRepository;
@Test
void testMyServiceMethod() {
// given
String expectedValue = "mocked value";
when(myRepository.getValue()).thenReturn(expectedValue);
// when
String actualValue = myService.getValueFromRepository();
// then
assertEquals(expectedValue, actualValue);
verify(myRepository).getValue(); // MyRepository의 getValue() 메소드 호출 검증
}
}
@Mock이란 무엇인가?
@Mock은 Mockito 테스팅 프레임워크에서 제공하는 어노테이션으로, 주로 단위 테스트에서 사용된다. @Mock으로 생성된 모의 객체는 스프링의 DI(Dependency Injection) 컨테이너에 의해 관리되지 않는다.
@Mock의 역할
단위 테스트용 모의 객체 생성
@Mock은 클래스 내부의 특정 의존성을 모의 객체로 대체할 때 사용된다. 이는 단위 테스트에서 특정 기능의 독립적 검증을 가능하게 한다.
직접적인 통제
Mockito에서 제공하는 다양한 기능(예: when, verify)을 이용하여 모의 객체의 행동을 직접 통제하고 검증할 수 있다.
@Mock 사용 예시
@ExtendWith(MockitoExtension.class)
@DisplayName("[단위] 회원가입 Service 테스트")
class SignUpServiceTest {
@Mock
private SignUpAdapter signUpAdapterMock;
@InjectMocks
private SignUpService sut;
@DisplayName("[happy] DB에 없는 이메일이 들어왔을때 true를 리턴한다.")
@Test
void checkEmailDuplicationTestSuccess() {
//given
String email = "test1@gmail.com";
when(signUpAdapterMock.isEmailAvailable(email)).thenReturn(true);
//when
boolean isEmailAvailable = sut.isEmailAvailable(email);
//then
assertThat(isEmailAvailable).isTrue();
}
}
🔥 여기서 잠깐! @Mock과 자주 사용되는 @InjectMocks에 대해서도 짚고 넘어가자!
@InjectMocks란 무엇인가?
@InjectMocks는 주로 @Mock과 함께 사용되며, Mockito 테스팅 프레임워크에서 제공하는 어노테이션이다. @InjectMocks는 테스트 대상 클래스에 @Mock으로 생성된 모의 객체를 자동으로 주입하기 위해 사용된다.
@InjectMocks와 @Mock의 사용
@InjectMocks는 테스트할 클래스의 인스턴스를 생성하고, 이 클래스의 의존성으로 선언된 필드에 @Mock으로 생성된 모의 객체를 주입하는데 사용된다. 이 과정은 Mockito에 의해 자동으로 이루어진다.
(@Mock 예시 코드와 동일한 코드다.)
@ExtendWith(MockitoExtension.class)
@DisplayName("[단위] 회원가입 Service 테스트")
class SignUpServiceTest {
@InjectMocks
private SignUpService sut;
@Mock
private SignUpAdapter signUpAdapterMock;
@DisplayName("[happy] DB에 없는 이메일이 들어왔을때 true를 리턴한다.")
@Test
void checkEmailDuplicationTestSuccess() {
//given
String email = "test1@gmail.com";
when(signUpAdapterMock.isEmailAvailable(email)).thenReturn(true);
//when
boolean isEmailAvailable = sut.isEmailAvailable(email);
//then
assertThat(isEmailAvailable).isTrue();
}
}
위 코드에서 SignUpService sut는 @InjectMocks에 의해 생성된 실제 SignUpService 클래스의 인스턴스다. 이 인스턴스는 테스트 중에 실제로 사용되는 객체이며, 내부 로직을 실행한다.
SignUpAdapter signUpAdapterMock은 @Mock으로 생성된 모의 객체다. sut 내에서 SignUpService가 SignUpAdapter를 사용할 때, 실제 SignUpAdapter 대신 이 모의 객체가 사용된다.
@InjectMocks의 역할과 사용
실제 객체 생성
@InjectMocks는 테스트하고자 하는 클래스(여기서는 SignUpService)의 실제 인스턴스를 생성한다.
의존성 주입
@InjectMocks로 생성된 인스턴스에는 해당 클래스가 의존하는 다른 컴포넌트들이 필요하다. 이 경우 @Mock으로 표시된 필드(예: SignUpAdapter)는 모의 객체로 생성되어, @InjectMocks로 생성된 SignUpService 인스턴스에 자동으로 주입된다.
🔥 결론
@InjectMocks는 테스트 대상 클래스의 실제 인스턴스를 생성하고, @Mock은 이 클래스가 의존하는 다른 컴포넌트를 모의 객체로 생성하는 데 사용된다. 이렇게 함으로써 테스트 중에는 SignUpService의 실제 로직이 실행되면서, 의존하는 컴포넌트는 제어 가능한 모의 객체로 대체되어 테스트의 격리성을 확보할 수 있다.
@MockBean과 @Mock 차이점
사용 환경
@MockBean은 통합 테스트에 적합하며, 스프링의 의존성 주입과 완전히 통합된다. 반면,@Mock은 단위 테스트에 적합하며, 보다 세밀한 모의 객체의 행동 제어가 가능하다.
스프링 컨텍스트
@MockBean으로 생성된 객체는 스프링 컨텍스트에 의해 관리되지만, @Mock으로 생성된 객체는 그렇지 않다. 다시 말해, @MockBean은 스프링 컨텍스트 전체에 영향을 미치는 반면, @Mock은 특정 테스트 내에서만 적용된다.
의도와 적용 범위
@MockBean과 @Mock은 모두 모의 객체를 생성하지만, @MockBean은 테스트 실행 중 스프링 컨텍스트에 존재하는 모든 동일 타입의 빈을 대체한다. @Mock은 특정 테스트 클래스 또는 메서드 내에서만 유효하다.
🔥 결론
@MockBean과 @Mock 이 두 어노테이션의 정확한 이해와 올바른 활용은 테스트 코드의 효율성과 정확성을 높이는 데 중요하다. @MockBean과 @Mock을 적절히 사용하여 각각의 테스트 시나리오에 맞는 최적의 테스트 환경을 구축할 수 있다.
회원가입 컨트롤러 테스트(Controller Test) - RequestDto @NotBlank 필드 검증
'Spring > Spring 테스트코드' 카테고리의 다른 글
테스트 격리성의 중요성과 모의 객체(Mock Object)를 활용한 전략 (3) | 2024.01.01 |
---|---|
스프링 테스트 코드에서 실제 호출 @SpyBean으로 확인하기 (3) | 2023.12.31 |
테스트 코드의 가독성 향상 - BDD 방법론과 @DisplayName 어노테이션 활용 (2) | 2023.12.26 |
테스트 전략에서 단위 테스트와 통합 테스트의 차이점과 의존성 관리 (1) | 2023.12.25 |
헥사고날 아키텍처와 TDD로 가는 길 (1) - 레이어 별 단위 테스트, 통합 테스트 전략 선택 (4) | 2023.12.24 |