Mock object (가짜 객체)

송영재·2022년 10월 30일

Spring

목록 보기
23/45

이상적으로, 각 테스트 케이스는 서로 분리되어야 한다. 이를 위해 가짜 객체(Mock object)를 생성하는 것도 좋은 방법이다.

출처: 단위 테스트 (위키백과)

  1. 분리 되기 어려운 클래스들

    • Controller 클래스만 테스트할 수 없을까?
      • 테스트 범위: Controller, Service, Repository
    • Service 클래스만 테스트할 수 없을까?
      • 테스트 범위: Service, Repository
    • Repository 클래스만 테스트할 수 없을까?
      • 테스트 범위: Repository
  2. 가짜객체 (Mock object) 를 통한 분리 방법

    • 가짜 객체 (Mock object) 로 분리
    • MockRepository
      • 실제 객체와 겉만 같은 객체!
        • 동일한 클래스명, 함수명
      • 실제 DB 작업은 하지 않음
        • DB 작업이 이뤄지는 것처럼~
        • 테스트를 위해 필요한 결과값을 return
    • 이하 간단히 'mock' (목) 이라고 부르기로 함
  3. Mock 직접 구현

    • [코드스니펫] test > mockobject > MockProductService
      import com.sparta.springcore.dto.ProductMypriceRequestDto;
      import com.sparta.springcore.dto.ProductRequestDto;
      import com.sparta.springcore.model.Product;
      
      import java.util.List;
      
      public class MockProductService {
      
          private final MockProductRepository mockProductRepository;
          public static final int MIN_MY_PRICE = 100;
      
          public MockProductService() {
              this.mockProductRepository = new MockProductRepository();
          }
      
          public Product createProduct(ProductRequestDto requestDto, Long userId ) {
              // 요청받은 DTO 로 DB에 저장할 객체 만들기
              Product product = new Product(requestDto, userId);
      
              mockProductRepository.save(product);
      
              return product;
          }
      
          public Product updateProduct(Long id, ProductMypriceRequestDto requestDto) {
              int myprice = requestDto.getMyprice();
              if (myprice < MIN_MY_PRICE) {
                  throw new IllegalArgumentException("유효하지 않은 관심 가격입니다. 최소 " + MIN_MY_PRICE + " 원 이상으로 설정해 주세요.");
              }
      
              Product product = mockProductRepository.findById(id)
                      .orElseThrow(() -> new NullPointerException("해당 아이디가 존재하지 않습니다."));
      
              product.setMyprice(myprice);
              mockProductRepository.save(product);
      
              return product;
          }
      
          // 회원 ID 로 등록된 상품 조회
          public List<Product> getProducts(Long userId) {
              return mockProductRepository.findAllByUserId(userId);
          }
      
          // (관리자용) 상품 전체 조회
          public List<Product> getAllProducts() {
              return mockProductRepository.findAll();
          }
      }
    • [코드스니펫] test > mockobject > MockProductRepository
      import com.sparta.springcore.model.Product;
      
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Optional;
      
      public class MockProductRepository {
      
          private List<Product> products = new ArrayList<>();
          // 상품 테이블 ID: 1부터 시작
          private Long productId = 1L;
      
          // 상품 저장
          public Product save(Product product) {
              for (Product existProduct : products) {
                  // 이미 저장된 상품 -> 희망 최저가 업데이트
                  if (existProduct.getId().equals(product.getId())) {
                      int myprice = product.getMyprice();
                      existProduct.setMyprice(myprice);
                      return existProduct;
                  }
              }
      
              // 신규 상품 -> DB 에 저장
              product.setId(productId);
              ++productId;
              products.add(product);
              return product;
          }
      
          // 상품 ID 로 상품 조회
          public Optional<Product> findById(Long id) {
              for (Product product : products) {
                  if (product.getId().equals(id)) {
                      return Optional.of(product);
                  }
              }
      
              return Optional.empty();
          }
      
          // 회원 ID 로 등록된 상품 조회
          public List<Product> findAllByUserId(Long userId) {
              List<Product> userProducts = new ArrayList<>();
              for (Product product : products) {
                  if (product.getId().equals(userId)) {
                      userProducts.add(product);
                  }
              }
      
              return userProducts;
          }
      
          // (관리자용) 상품 전체 조회
          public List<Product> findAll() {
              return products;
          }
      }
    • [코드스니펫] test > mockobject > MockProductServiceTest
      import com.sparta.springcore.dto.ProductMypriceRequestDto;
      import com.sparta.springcore.dto.ProductRequestDto;
      import com.sparta.springcore.model.Product;
      import org.junit.jupiter.api.DisplayName;
      import org.junit.jupiter.api.Test;
      
      import static com.sparta.springcore.service.ProductService.MIN_MY_PRICE;
      import static org.junit.jupiter.api.Assertions.assertEquals;
      import static org.junit.jupiter.api.Assertions.assertThrows;
      
      public class MockProductServiceTest {
          @Test
          @DisplayName("관심 상품 희망가 - 최저가 이상으로 변경")
          void updateProduct_Normal() {
              // given
              int myprice = MIN_MY_PRICE + 1000;
      
              ProductMypriceRequestDto requestMyPriceDto = new ProductMypriceRequestDto(
                      myprice
              );
      
              Long userId = 777L;
              ProductRequestDto  requestProductDto = new ProductRequestDto(
                      "오리온 꼬북칩 초코츄러스맛 160g",
                      "https://shopping-phinf.pstatic.net/main_2416122/24161228524.20200915151118.jpg",
                      "https://search.shopping.naver.com/gate.nhn?id=24161228524",
                      2350
              );
      
              MockProductService mockProductService = new MockProductService();
              // 회원의 관심상품을 생성
              Product product = mockProductService.createProduct(requestProductDto, userId);
      
              // when
              Product result = mockProductService.updateProduct(product.getId(), requestMyPriceDto);
      
              // then
              assertEquals(myprice, result.getMyprice());
          }
      
          @Test
          @DisplayName("관심 상품 희망가 - 최저가 미만으로 변경")
          void updateProduct_Failed() {
              // given
              int myprice = MIN_MY_PRICE - 50;
      
              ProductMypriceRequestDto requestMyPriceDto = new ProductMypriceRequestDto(
                      myprice
              );
      
              Long userId = 777L;
              ProductRequestDto  requestProductDto = new ProductRequestDto(
                      "오리온 꼬북칩 초코츄러스맛 160g",
                      "https://shopping-phinf.pstatic.net/main_2416122/24161228524.20200915151118.jpg",
                      "https://search.shopping.naver.com/gate.nhn?id=24161228524",
                      2350
              );
      
              MockProductService mockProductService = new MockProductService();
              // 회원의 관심상품을 생성
              Product product = mockProductService.createProduct(requestProductDto, userId);
      
              // when
              Exception exception = assertThrows(IllegalArgumentException.class, () -> {
                  mockProductService.updateProduct(product.getId(), requestMyPriceDto);
              });
      
              // then
              assertEquals(
                      "유효하지 않은 관심 가격입니다. 최소 " + MIN_MY_PRICE + " 원 이상으로 설정해 주세요.",
                      exception.getMessage()
              );
          }
      }

0개의 댓글