[SpringBootTest] Mockito @Mock @MockBean @Spy @SpyBean

hyozkim·2021년 9월 17일
1

SpringBootTest

목록 보기
1/4
post-thumbnail

들어가면서 👋

요즘 테스트 코드를 작성하는 습관을 들이기도 싶어졌고, 테스트 코드를 작성하면서 자연스럽게 Spring Rest Docs도 같이 사용해보게 되었다.
(웹 UI에서 테스트를 바로 할 수 있는 장점은 아쉽지만 Swagger를 걷어내면 코드가 더 깔끔해져질까 싶음)

@Mock
@Spy
@InjectMocks

@MockBean
@SpyBean

Mock, Spy는 활용도가 굉장히 높다.
1) 테스트해야할 메소드의 validation 을 무시하도록 설정하기도 하고,
2) 외부 API와 굳이 연동할 필요 없이 테스트를 수행하기도 하고,
3) DB에 데이터 입력 없이 테스트를 수행하기도 하고,
4) 불필요한 필드 데이터는 채우지 않고도 테스트를 수행할 수도 있습니다.

물론 모든 테스트를 다 Mock, Spy만으로 할수는 없다.
통합 테스트 코드는 분명 필요해서 모든 메소드를 정상코드로 테스트하는 코드도 필요하지만,
단위 테스트로 쪼개서 테스트할때는 Mock, Spy를 적극적으로 활용하자.

Mockito+Rest Docs 테스트 코드를 작성하면서 고민이 되었던 내용도 함께 정리하려고 한다.

@Mock @Spy @InjectMocks

Mockito에서 제공하는 기능으로 간단하게 설명하면 빈껍데기로 만든다고 이해하면 된다.

시나리오에 따라 테스트하는 방법인 BDD(given, when, then 순)가 있고,

TDD 로 테스트 코드를 작성하는 방법 등 기호에 따라 여러가지 테스트 코드 작성 방법이 있다.

@Mock
mock 객체를 만들어 반환 (실제 인스턴스 없이 가상의 mock 인스턴스를 직접 만들어 사용)

@Spy
spy 객체를 만들어 반환 (실제 인스턴스를 사용해서 mocking함, Spy 객체는 행위given when stubbing하지 않으면 실제 인스턴스의 메서드를 호출한다.)

@InjectMocks
Mock 객체 주입(Injection)
@Mock이나 @Spy 객체를 자신의 멤버 클래스와 일치하면 주입

@MockBean
Spring ApplicationContext에 mock객체를 추가

@SpyBean
Spring ApplicationContext에 spy객체를 추가

@MockBean

spring-boot-test 패키지는 Mockito를 포함하고 있다.

따라서 기존에 사용하던 방식대로 Mock 객체를 생성해서 테스트하는 방법도 있지만, spring-boot-test 에서는 새로운 방법도 제공하고 있음. 따라서 @MockBean 어노테이션을 사용해서 이름 그대로 Mock 객체를 빈으로써 등록할 수 있음!

이렇게 @MockBean으로 선언된 빈을 주입받는다면 Spring의 ApplicationContext는 Mock 객체를 주입해준다.

새롭게 @MockBean을 선언하면 Mock 객체를 빈으로써 등록하는데,,,

여기서 주의하게 보아야 할 점은 만일 @MockBean으로 선언한 객체와 같은 이름과 타입으로 이미 빈으로 등록되어있다면 해당 빈은 선언한 Mock Bean 객체로 대체된다는 것이다.

@SpyBean

SpyBean도 spring-boot-test 패키지에 Mockito를 포함하고 있음.

@SpyBean은 given에서 선언한 코드 외에는 전부 실제 객체의 것을 사용합니다.
이미 존재하는 Bean을 SpyBean으로 Wrapping한 형태라고 생각하시면 됩니다.

내가 헤맸던(?) 부분

@MockBean @Autowired 우선순위

@MockBean
private TestService testService;

@Autowire
private TestService testService;

위와 같이 주입을 했다면 TestService 객체는 어떻게 될까?

Spring에서는 같은 타입과 이름으로 이미 빈이 등록되어 있다면 @MockBean으로 다 대체한다.

따라서 TestService 객체는 Mocking이 되어 @MockBean이 된다.

Toast Meeup 내용 참고
@MockBean 어노테이션을 사용해서 이름 그대로 Mock 객체를 빈으로써 등록할 수 있습니다. 그렇기 때문에 만일 @MockBean으로 선언된 빈을 주입받는다면(@Autowired 같은 어노테이션 등을 통해서) Spring의 ApplicationContext는 Mock 객체를 주입해줍니다.

새롭게 @MockBean을 선언하면 Mock 객체를 빈으로써 등록하지만, 만일 @MockBean으로 선언한 객체와 같은 이름과 타입으로 이미 빈으로 등록되어있다면 해당 빈은 선언한 Mock 빈으로 대체됩니다.

난 Mocking 데이터(예상 반환)를 사용하고, 동시에 실제 DB에서 데이터를 가져와 이를 비교하는 로직으로 구현하고 싶었다.


이를 위해 TestService 같은 타입과 이름으로는 따로 만들어 봤자 MockBean으로 설정되니, 실제 DB에 있는 데이터를 조회하기 위해 TestRepository를 통해 데이터를 조회했다.

@Autowired
private TestRepository testRepository;

코드 설명

// given
given(mockTestService.getTestList(searchCount,searchPage,sortType))
		.willReturn(getTestList(searchCount,searchPage,sortType));

getTestList()

@Autowired
private TestRepository testRepository;

private OnSaleGoodResponseDto getTestList(int searchCount, int searchPage, String sortType) {
  List<TestGood> testGoodList = testRepository.getTestList();

  testGoodList = testGoodList.stream().map(testGood -> {
      ...
      ...

  }).collect(Collectors.toList());;
  
  return testGoodList;
}

참고

baeldung Spring Mockito
NHN MeetUp
기억보다 기록을

profile
차근차근 develog

1개의 댓글

comment-user-thumbnail
2023년 8월 8일

잘 읽었습니다. 감사합니다.

답글 달기