Service - 가짜환경 Mock으로 테스트

jihan kong·2022년 9월 19일
0

JUnit5

목록 보기
16/25
post-thumbnail

본 시리즈는 메타 코딩님의 Junit 강의를 학습한 내용을 바탕으로 정리하였습니다.

저번 포스팅에서 Service 레이어만 독자적으로 테스트하기 위해 Mock 라이브러리를 통해 테스트를 구현한다고 했었다. 실제로 구현해보자.

가짜 환경 Mock 을 이용한 책 등록하기 테스트

BookServiceTest.java

package site.metacoding.junitproject.service;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import site.metacoding.junitproject.domain.BookRepository;
import site.metacoding.junitproject.util.MailSender;
import site.metacoding.junitproject.util.MailSenderStub;
import site.metacoding.junitproject.web.dto.BookRespDto;
import site.metacoding.junitproject.web.dto.BookSaveReqDto;

@ExtendWith(MockitoExtension.class) // 1. 
public class BookServiceTest {

    @InjectMocks					// 2. 
    private BookService bookService;

    @Mock							// 3.
    private BookRepository bookRepository;

    @Mock							
    private MailSender mailSender;

    @Test
    public void 책등록하기_테스트() {

        // given
        BookSaveReqDto dto = new BookSaveReqDto();
        dto.setTitle("Junit 강의");
        dto.setAuthor("메타코딩");

        // stub
      

        // when
        BookRespDto bookRespDto = bookService.책등록하기(dto);

        // then
        assertEquals(dto.getTitle(), bookRespDto.getTitle());
        assertEquals(dto.getAuthor(), bookRespDto.getAuthor());
    }
    
}

  1. MockitoExtension 메서드를 이용해 BookServiceTest 를 가짜 메모리 환경으로 만든다.
  2. 테스트 대상에 @InjectMocks 를 붙여준다.
  3. inject 받는 객체들에@Mock 어노테이션을 붙여준다.

그럼 이제 테스트를 돌려보면...

😱...!
위와 같이 NullPointerException 이 터지는 것을 볼 수 있다. 왜 Null 값이 리턴되는 예외가 발생할까? 이는 조금만 생각해보면 당연한 일이다.

우리는 @InjectMocks 을 통해 service 레이어를 Mock으로 가져왔다. 근데 그 뿐이지 Mocking 을 통해 어떤 것을 수행할지는 정의해주지 않은 것이다.

따라서 우리는 행동을 정의해줄 필요성이 있다. 우리에게는 stub(가정) 이 있다. 이렇게 작성해보자.


Stub 정의

BookService.java

    @Test
    public void 책등록하기_테스트() {

        // given
        BookSaveReqDto dto = new BookSaveReqDto();
        dto.setTitle("Junit 강의");
        dto.setAuthor("메타코딩");

        // stub (가설)
        when(bookRepository.save(any())).thenReturn(dto.toEntity());  // 1.
        when(mailSender.send()).thenReturn(true);                     // 2.

        // when
        BookRespDto bookRespDto = bookService.책등록하기(dto);
    }
    
}

우리의 Stub 은 다음을 상정하고 있다.

  1. bookRepository 즉, 실제 DB에 저장되는 어떤 값이든(any) 실제 책 등록이 일어날 때, 그것은 BookService.java 의 값이 저장되어 리턴되는 것임을 의미한다.
  2. mailSender 클래스에서 send 메소드가 실행되는 것은 mailSender 의 boolean 값 true가 return 되는 것과 같음을 의미한다.

Stub(가설) 에 대한 설명

위의 1번이 뭘 말하는 것일까?
실제, BookService.java 의 코드를 잠깐 살펴보면...

    // 1. 책 등록
    @Transactional(rollbackOn = RuntimeException.class)
    public BookRespDto 책등록하기(BookSaveReqDto dto) {
>         Book bookPS = bookRepository.save(dto.toEntity());
        if (bookPS != null) {
        
        // (...생략)
    }

초록색 화살표의 코드 라인을 보면 실제 bookService 에서 save 를 진행할 때, 우리는 bookRepositoryMock 해놓았기 때문에(가짜로 만들었기 때문에) 실제 값이 들어갈 수가 없다.

그렇다고 테스트를 못한다면 안되는 일.. bookRepository 에 어떤 값이 들어가든지간에 리턴되는 값은 bookPSdto 값이다라고 테스트를 작성한 것이다.

이로써 우리는 가짜환경을 구축하고 그 환경 위에서 테스트를 진행할 수 있게 되었다.


검증

이에 대한 검증은 지금까지 우리가 쓰던 assertEquals 보다 더 가독성이 좋고 여러 기능들이 포함된 AssertJ 라이브러리의 assertThat 을 사용해서 검증한다.

        
        // (..생략)
        
        // then
        // assertEquals(dto.getTitle(), bookRespDto.getTitle());
        // assertEquals(dto.getAuthor(), bookRespDto.getAuthor());
        assertThat(dto.getTitle()).isEqualTo(bookRespDto.getTitle());
        assertThat(dto.getAuthor()).isEqualTo(bookRespDto.getAuthor());
    }
    
}

📌 참고
assertj 라이브러리를 사용하기 위해선 build.gradle 설정 파일에 testImplementation("org.assertj:assertj-core:3.23.1") 을 추가해주고 반영해야한다.

Assertions 사용을 위해선 필수 라이브러리인 AssertJ 의 특징과 여러 기능들이 궁금하다면❓
daleseo님의 블로그 (https://www.daleseo.com/assertj/)

profile
학습하며 도전하는 것을 즐기는 개발자

0개의 댓글