-
Service Layer의 단위테스트 작성 : (1) Mockito ①Spring 2022. 1. 28. 09:40
서비스 계층을 테스트 하기 위해서 실제 Repository를 DI하게 된다면 단위테스트를 작성하기 위한 F.I.R.S.T원칙을 위배하게 된다.
- Fast : @SpringBootTest 어노테이션이 해당 어플리케이션이 모든 빈을 Ioc 컨테이너에 등록하고 테스트를 진행하므로 테스트가 느려진다.
- Independent : repository와 의존관계를 갖는 테스트 를 작성하게 된다면 테스트 대상이 Service계층의 테스트는 Repository에 의존적이 되므로 독립적인 테스트작성이 어려워 진다.
- Repeatable : DB에 의존적인 테스트는 한번 ID가 생기면 중복되므로 반복 테스트가 어렵다.
테스트 어노테이션
@SpringBootTest 통합테스트, 전체 Bean전체 @WebMvcTest 단위테스트, MVC 테스트 MVC와 관련된 Bean @DataJpaTest 단위테스트, JPA 테스트 JPA와 관련된 Bean @RestClientTest 단위테스트, REST API 테스트 일부 Bean @JsonTest 단위테스트, Json 테스트 일부 Bean @SpringBootTest
@RunWith(SpringRunner.class)와 함께 통합테스트를 위한 스프링 부트 테스트 어노테이션으로 어플리케이션 실행 설정을 바꿔서 테스트 하는 것이 가능하며, 실제 구동되는 어플리케이션과 동일한 컨텍스트를 가져 일대일 대응 수준의 테스트가 가능하다. 따라서 테스트를 실행하기 위해 어플리케이션 설정을 모두 로드하기 때문에 규모가 클수록 느리게 작동된다.
Mokito
테스트에 들어가는 객체의 동작을 직접 제어할 수 있도록 하는 가짜객체 를 지원하는 테스트 프레임 워크이다. Mock객체의 의존성을 주입함으로서 분리되고 독립적인 테스트 코드 작성이 가능해진다. SpringBootTest에서 Mockito를 사용하고 싶을 때에는 @ExtendWith(MockitoExtension.class) 어노테이션을 추가하여 사용하도록 한다.
Mock 객체 의존성 주입
가짜 객체를 사용하기 위해서는 다음과 같은 어노테이션을 사용한다.
- @Mock : Mock 객체를 만들어 반환해주는 어노테이션으로 Mock으로 만들 객체에 붙여준다.
- @Spy : 지정해주지 않은 부분은 그대로 객체를 사용하도록 하는 어노테이션
- @InjectMocks : Mock, Spy로 생성된 가짜 객체를 주입시켜 사용할 객체에 붙여주는 어노테이션 (테스트 대상)
가짜 객체를 주입했다면 다음과 같은 메소드를 사용하여 가짜 결과를 반환하도록 지정해준다. ( = Stub )
- doReturn() : Mock객체가 특정한 값을 반환 ( = thenReturn() )
- doNothing() : Mock객체가 아무것도 반환하지 않음
- doThrow() : Mock 객체가 예외를 발생시킴
- given(가짜객체.메소드)
- willReturn() : 특정 메소드 실행시 특정 값 리턴
- willThrow() : 특정 메소드 실행시 예외 발생
doReturn(java.util.Optional.of(activity)).when(activityRepository).findById(anyInt()); when(activityRepository.findById(anyInt())).thenReturn(java.util.Optional.of(activity));
여기서 doReturn과 thenReturn의 차이는 다음과 같다.
- doReturn() : 실제 메소드를 호출하지 않으면서 리턴값을 임의로 정의 ( 실제 메소드를 호출하지 않기 때문에 대상 메서드에 문제가 있어도 알 수 없다)
- thenReturn() : 실제 메소드를 호출하지만 임의로 리턴값을 정의 ( 실제 메소드를 호출하기 때문에 메소드 작업이 오래 걸릴 경우 시간이 길어지지만 대상 메소드에 문제가 존재한다면 알 수 있다 ( 이것을 쓰도록 하자) )
Mock 객체 적용
@RunWith(SpringRunner.class) @ExtendWith(MockitoExtension.class) @SpringBootTest @Transactional class UserActivityServiceTests { @InjectMocks private UserActivityService userActivityService; @MockBean private UserActivityRepository userActivityRepository; @MockBean private ActivityRepository activityRepository; @MockBean private UserRepository userRepository; //... @Test @DisplayName("주어진 상태에 따른 새로운 사용자 액티비티를 생성하고 사용자 레벨을 조정한다.") void create_user_activity() { when(userRepository.findById(anyInt())).thenReturn(java.util.Optional.of(user)); doReturn(java.util.Optional.of(activity)).when(activityRepository).findById(anyInt()); // when(activityRepository.findById(anyInt())).thenReturn(java.util.Optional.of(activity)); UserActivityResponse response = userActivityService.create(userActivityRequest); //then assertEquals... } @Test @DisplayName("범위 밖의 점수로는 리뷰를 생성하지 않는다.") void 예외경우 테스트(){ assertThatThrownBy(() -> reviewService.create(request)) .isInstanceOf(InvalidRatingException.class); } }
위와 같이 가짜 객체를 사용할 대상에 @Mock 어노테이션을, 가짜 객체를 주입할 대상에 (테스트 할 대상) @InjectsMocks를 해줌으로서 독립적이고 반복가능한 테스트를 작성할 수 있다.
( 참고한 사이트 )
https://byeongyeon.tistory.com/85
[Unit Test] Mockito
[ 마틴 엉클 밥 선생님의 단위테스트 말씀(F.I.R.S.T) ] Fast 단위테스트는 가능한 빠르게 실행되어야 한다.(실행에 있어 느린 테스트는 꺼리게 된다면 잘못 만든 테스트다) @SpringBootTest 어노테이션
byeongyeon.tistory.com
https://pangtrue.tistory.com/228
[Spring Boot] @SpringBootTest
먼저, 테스트를 위한 몇 가지 어노테이션에 대해 살펴보자 어노테이션 설명 Bean @SpringBootTest 통합 테스트, 전체 Bean 전체 @WebMvcTest 단위 테스트, Mvc 테스트 MVC 관련된 Bean @DataJpaTest 단위 테스트,..
pangtrue.tistory.com
https://royleej9.tistory.com/entry/Mockito-doReturn-thenReturn
[Mockito] doReturn / thenReturn
공통점 테스트 대상 기능 중 일부 기능이 준비가 되지 않았을때 / 다른 시스템과 통신등이 필요한 경우 미리 리턴 값을 선언하여 테스트 할수 있다. / Stub when(spyWhenService.callServerAPI("1")).thenReturn(t.
royleej9.tistory.com
https://jojoldu.tistory.com/226
SpringBoot @MockBean, @SpyBean 소개
안녕하세요? 이번 시간엔 SpringBoot의 @MockBean , @SpyBean 예제를 진행해보려고 합니다. 모든 코드는 Github에 있기 때문에 함께 보시면 더 이해하기 쉬우실 것 같습니다. (공부한 내용을 정리하는 Github
jojoldu.tistory.com
https://stackoverflow.com/questions/3459287/whats-the-difference-between-a-mock-stub
What's the difference between a mock & stub?
I've read various articles about mocking vs stubbing in testing, including Martin Fowler's Mocks Aren't Stubs, but still don't understand the difference.
stackoverflow.com
https://stackoverflow.com/questions/61613131/testing-service-layer-in-spring-boot-using-mockito
Testing service layer in Spring Boot using Mockito
I am trying to unit test my service layer using mockito having a connection to a Database through the Repository layer. My test case: @InjectMocks private EmployeeService employeeService; @Mock
stackoverflow.com
https://stackoverflow.com/questions/51053469/unit-tests-how-to-mock-repository-using-mockito
Unit Tests How to Mock Repository Using Mockito
I am having an issue with stubbing my repository. I was suggested to just create another application.properties (which I have not done) and to use an in-memory database like H2. I was wondering tho...
stackoverflow.com
https://velog.io/@hellonayeon/spring-boot-service-layer-unit-testcode
[Spring Boot] Service 계층의 단위 테스트 코드 작성
velog.io
https://velog.io/@dnjscksdn98/JUnit-Mockito-Verify-Method-Calls
[JUnit & Mockito] Verify Method Calls
조금 더 견고하고 정확한 테스트를 진행하기 위해서 가끔은 해당 테스트 안에서 특정 메소드를 호출했는지에 대해서 검증을 할 필요가 있습니다. 이를 위해 Mockito 에서는 verify() 라는 함수를 지
velog.io
'Spring' 카테고리의 다른 글
Service Layer의 단위테스트 작성 : (2) Fake 객체 (0) 2022.02.03 Service Layer의 단위테스트 작성 : (1) Mockito ② (0) 2022.02.03 예외처리전략 ( ExceptionHandler ) 2 (0) 2022.01.13 JPA 엔티티에 생성 시점, 수정 시점 Timestamp 추가하기 ( + Auditing) (0) 2022.01.13 JPA getById() 와 findById()의 차이 (0) 2022.01.12