Spring 심화반 - 3주차 - 1

2022년 4월 16일(토)
[스파르타코딩클럽] Spring 심화반 - 3주차 - 1

◎ 테스트

  • 개발하면서 버그는 필수불가결한 존재이다. 따라서 배포 전 최대한 버그를 많이 찾아내야 한다. 버그는 어디서 발생할 지 모르므로 가장 효율적으로 찾아내는 것이 중요

  • 테스트의 필요성

    • 버그 발견 시간에 따라 비용이 기하급수적으로 커지므로, 배포 전 최대한 버그를 많이 찾아내야 한다.
  • 테스트의 종류

    1. 단위 테스트 (Unit Test)
      • 하나의 모듈이나 클래스에 대해 세밀한 부분까지 테스트 가능
      • 모듈 간에 상호 작용 검증 못함
    2. 통합 테스트 (Integration Test)
      • 두 개 이상의 모듈이 연결된 상태를 테스트
      • 모듈 간의 연결에서 발생하는 에러 검증 가능
    3. E2E 테스트 (End to End Test) (=블랙박스 테스팅)
      • 실제 사용자의 실행 환경과 거의 동일한 환경에서 테스트 진행
  • Spring의 test Framework를 통해 여러 Edge 케이스들을 쉽고 빠르게 테스트 할 수 있다. (이와 같은 방법에는 여러 장단점이 존재하므로, 상황에 맞춰 사용)

◎ JUnit 을 이용한 단위 테스트

  • 단위 테스트: 프로그램을 작은 단위로 쪼개서 각 단위가 정확하게 동작하는지 검사하고 이를 통해 문제 발생 시 정확하게 어느 부분이 잘못되었는지를 재빨리 확인할 수 있게 해준다.

  • JUnit 사용 설정: build.gradle에 JUnit 사용을 위한 환경설정이 이미 되어있다.

testImplementation 'org.springframework.boot:spring-boot-starter-test'
  • 테스트 파일 만들기:
    • 해당 파일 내에서 마우스 오른쪽 버튼 클릭 > "Generate..." > "Test..." >기본세팅으로 생성
    • src > test > java 패키지 아래에 Class 생성
  • 간단한 테스트 코드 예시
// Product가 잘 되어있는지 확인
class ProductTest {
    @Test // 테스트용 함수에 표기
    @DisplayName("정상 케이스") // 테스트 결과 볼 때, 해당 이름으로 표기
    void createProduct_Normal() {
        // given
        Long userId = 100L;
        String title = "오리온 꼬북칩 초코츄러스맛 160g";
        String image = "https://shopping-phinf.pstatic.net/main_2416122/24161228524.20200915151118.jpg";
        String link = "https://search.shopping.naver.com/gate.nhn?id=24161228524";
        int lprice = 2350;

        ProductRequestDto requestDto = new ProductRequestDto(
                title,
                image,
                link,
                lprice
        );

        // when
        Product product = new Product(requestDto, userId);

        // then
        assertNull(product.getId());  // 빈 값인지 확인
        assertEquals(userId, product.getUserId()); // assertEquals(예상값, 실제값)
        assertEquals(title, product.getTitle());
        assertEquals(image, product.getImage());
        assertEquals(link, product.getLink());
        assertEquals(lprice, product.getLprice());
        assertEquals(0, product.getMyprice());
    }
}
  • 수행 결과가 한글로 표시되지 않는 경우
    -> File - Settings - 검색창에 "gradle" 입력

◎ Edge 케이스를 고려한 단위 테스트

  • 클라이언트에서 들어오는 값을 고려하여 데이터를 보내주더라도, 서버에서 한번더 고려해줄 필요가 있다.
    ex) 상품 이미지가 URL 포멧이 아닌 경우, 회원 ID가 null 이거나 "" 일 경우, 상품명이 없는 경우...

  • 따라서 DB와 긴밀히 연관된 Class에 경우, 값을 예외처리 하고, 해당하지 않으면 오류를 낼 것 (Exception을 따로 만들면 Case에 따라 쉽게 구분할 수 있다.)

  // 예시
  // 입력값 Validation
        if (userId == null || userId <= 0) {
            throw new IllegalArgumentException("회원 Id 가 유효하지 않습니다.");
        }
  • Test 설계시, Edge Case들을 설계하여, 해당 오류가 발생하는지 체크.
// key annotation
@Nested // 클래스에서 생성, 하위에 함수를 작성하여 설계
@BeforeEach // 각 Test 전에 해당 행동 실행

// 예시
class ProductTest {

    @Nested
    @DisplayName("회원이 요청한 관심상품 객체 생성")
    class CreateUserProduct {

        private Long userId;
        private String title;
        private String image;
        private String link;
        private int lprice;

        @BeforeEach
        void setup() {
            userId = 100L;
            title = "오리온 꼬북칩 초코츄러스맛 160g";
            image = "https://shopping-phinf.pstatic.net/main_2416122/24161228524.20200915151118.jpg";
            link = "https://search.shopping.naver.com/gate.nhn?id=24161228524";
            lprice = 2350;
        }

        @Test
        @DisplayName("정상 케이스")
        void createProduct_Normal() {
            // given
            ProductRequestDto requestDto = new ProductRequestDto(
                    title,
                    image,
                    link,
                    lprice
            );

            // when
            Product product = new Product(requestDto, userId);

            // then
            assertNull(product.getId());
            assertEquals(userId, product.getUserId());
            assertEquals(title, product.getTitle());
            assertEquals(image, product.getImage());
            assertEquals(link, product.getLink());
            assertEquals(lprice, product.getLprice());
            assertEquals(0, product.getMyprice());
        }

◎ TDD (Test-Driven Development)

  • AS-IS) 설계 → 개발 → 테스트 (→ 설계 수정) 순서를
  • TO-BE) 설계 → 테스트 (→설계 수정) → 개발로 변경
    -> 관련 내용은 구글링 할 것 (강사님은 직접 한번 해보고, 어떨게 다른지 느끼는 것을 추천함)

◎ Mock object 직접 구현을 통한 단위 테스트

  • ProductService의 단위 테스트를 시도시 Repository가 필요.
    • 이상적으로, 각 테스트 케이스는 서로 분리되어야 한다. -> 가짜 객체의 필요성 있음
    • MockService
      • 실제 Service에서 이름만 대체하여 MockService를 만듦
    • MockRepository:
      • 실제 Repository와 겉만 같은 객체(동일한 클래스명, 동일한 함수명)
      • DB 작업에서 하는 내용의 Input 값과 return 값만 동일 (실제 DB 작업은 하지 않음)
    • Test : MockService, MockRepository를 이용하여 테스트
  • 단점

    • Bean, IoC를 이용할 수 없다.
    • Service 수정시, MockService, MockRepository를 대폭 수정해야 함
  • 쉽게 Mock을 만들어 사용할 수 있는 Mockito를 이용하는 것이 실용성이 높음.

좋은 웹페이지 즐겨찾기