[Test] SpringBoot Test - Service (Mockito)

Unit Test ?

단위 테스트는 Service Layer의 비즈니스 로직만을 검증하는 것이므로, Repository와 Controller의 영향을 받지 않는 환경에서 테스트를 진행하여야 한다.

테스트의 F.I.R.S.T 원칙

F - Fast: 테스트를 시도하는 것에 있어서 주춤되지 않을 만큼 빨라야 한다.

I - Independent: 객체의 상태, 메소드, 이전 테스트 상태, 다른 메소드의 결과에 의존해서는 안된다.

R - Repeatable: 어떤 환경에서도 같은 결과가 나올 수 있도록 반복 가능해야 한다.

S - Self-Validating: 모든 테스트는 pass 혹은 fail의 결과만 가지고 있어야 한다.

T - Timely: 실제 코드가 만들어지기 전에 테스트 되어야 한다. 실제 코드를 구현한 후 테스트 코드를 만들면, 테스트가 불가능하도록 실제 코드를 설계할지도 모른다.

Mockito

자바에서 단위테스트를 하기 위해 Mock을 만들어주는 프레임워크

Mock

실제 객체를 만들어 사용하기에 시간, 비용등의 Cost가 높거나 혹은 객체 서로간의 의존성이 강해 구현이 힘들 경우 가짜 객체를 만들어 사용하는 방법

사용법: https://github.com/mockito/mockito/wiki/Mockito-features-in-Korean

Service Test 작성

@ExtendWith(MockitoExtension.class)
class SafetyInfoServiceTest {
    @InjectMocks
    private SafetyInfoServiceImpl safetyInfoService;

    @Mock
    private SafetyInfoRepository safetyInfoRepository;

    @Mock
    private UserRepository userRepository;

    @Mock
    private SafetyInfoQueryRepository safetyInfoQueryRepository;

    final String userEmail = "[email protected]";

    void setup(){
        User user = User.builder().build();
        given(userRepository.findByEmail(userEmail)).willReturn(Optional.of(user));
    }

    @Test
    void findAllSafetyInfo() {
        // given
        setup();
        List<FindSafetyInfoResDto> expected = new ArrayList<>();
        given(safetyInfoQueryRepository.findAllWithUserId(any())).willReturn(expected);

        // when
        List<FindSafetyInfoResDto> response = safetyInfoService.findAllSafetyInfo(userEmail);

        // then
        assertThat(response).isEqualTo(expected);
    }
}

@ExtendWith(MockitoExtension.class)

SpringContainer를 로드하지 않고(=Spring에 의존하지 않음) 필요한 객체만 실행함 -> 매우 빠름! 

@Mock, @InjectMocks

- @Mock: 테스트에 '필요한' 가짜 객체

- @InjectMocks: 의존성 주입이 필요한 Mock 객체

@Mock으로 선언된 가짜 객체들을 의존한 @InjectMocks 객체를 생성!

given(a(b)).willReturn(c)

메소드가 실행되었을 때의 테스트를 위한 상황을 설정

- a: Mocking 할 메소드

- b: 메소드의 파라미터

- c: 메소드를 수행했을 때 반환했다고 설정할 값

when.thenReturn VS given.willReturn

when: Mockito를 import

given: BDDMockito를 import

차이: BDD 방식을 코드에 도입할 때, 기존의 Mockito가 가독성을 해치기 때문에 이를 해결하기 위한 기능은 같지만 이름이 다른 클래스!