Spring/팀스파르타

44. Mockito

열심히 해 2024. 12. 6. 14:56

 

  • Controller 클래스만 테스트할 수 없을까요?
    • 테스트 범위: Controller, Service, Repository
  • Service 클래스만 테스트할 수 없을까요?
    • 테스트 범위: Service, Repository
  • Repository 클래스만 테스트할 수 없을까요?
    • 테스트 범위: Repository

 

 

아래 서비스 단에 있는 updateProduct 메서드를 테스트해보려 합니다.

 

@Service
public class ProductService {
    // ...

    public static final int MIN_MY_PRICE = 100;

    // ...

    @Transactional
    public Product updateProduct(Long id, ProductMypriceRequestDto requestDto) {
        int myprice = requestDto.getMyprice();
        if (myprice < MIN_MY_PRICE) {
            throw new IllegalArgumentException("유효하지 않은 관심 가격입니다. 최소 " + MIN_MY_PRICE + " 원 이상으로 설정해 주세요.");
        }

        Product product = productRepository.findById(id).orElseThrow(() -> new NullPointerException("해당 상품을 찾을 수 없습니다."));

        product.setMyprice(myprice);

        return product.getId();
    }

    // ...

}

 

 

 

  • 가짜 객체 (Mock object) 로 분리합니다.
  • MockRepository
    • 실제 객체와 겉만 같은 객체입니다.
      • 동일한 클래스명, 함수명
    • 실제 DB 작업은 하지 않습니다.
      • DB 작업이 이뤄지는 것처럼 테스트를 위해 필요한 결과값을 return 합니다.

 

@ExtendWith(MockitoExtension.class) // @Mock 사용을 위해 설정합니다.
class ProductServiceTest {

    @Mock
    ProductRepository productRepository;

    @Mock
    FolderRepository folderRepository;

    @Mock
    ProductFolderRepository productFolderRepository;

    @Test
    @DisplayName("관심 상품 희망가 - 최저가 이상으로 변경")
    void test1() {
        // given
        Long productId = 100L;
        int myprice = ProductService.MIN_MY_PRICE + 3_000_000;

        ProductMypriceRequestDto requestMyPriceDto = new ProductMypriceRequestDto();
        requestMyPriceDto.setMyprice(myprice);

        User user = new User();
        ProductRequestDto requestProductDto = new ProductRequestDto(
                "Apple <b>맥북</b> <b>프로</b> 16형 2021년 <b>M1</b> Max 10코어 실버 (MK1H3KH/A) ",
                "https://shopping-phinf.pstatic.net/main_2941337/29413376619.20220705152340.jpg",
                "https://search.shopping.naver.com/gate.nhn?id=29413376619",
                3515000
        );

        Product product = new Product(requestProductDto, user);

        ProductService productService = new ProductService(productRepository, folderRepository, productFolderRepository);

        given(productRepository.findById(productId)).willReturn(Optional.of(product));

        // when
        ProductResponseDto result = productService.updateProduct(productId, requestMyPriceDto);

        // then
        assertEquals(myprice, result.getMyprice());
    }

    @Test
    @DisplayName("관심 상품 희망가 - 최저가 미만으로 변경")
    void test2() {
        // given
        Long productId = 200L;
        int myprice = ProductService.MIN_MY_PRICE - 50;

        ProductMypriceRequestDto requestMyPriceDto = new ProductMypriceRequestDto();
        requestMyPriceDto.setMyprice(myprice);

        ProductService productService = new ProductService(productRepository, folderRepository, productFolderRepository);

        // when
        Exception exception = assertThrows(IllegalArgumentException.class, () -> {
            productService.updateProduct(productId, requestMyPriceDto);
        });

        // then
        assertEquals(
                "유효하지 않은 관심 가격입니다. 최소 " +ProductService.MIN_MY_PRICE + " 원 이상으로 설정해 주세요.",
                exception.getMessage()
        );
    }
}

@ExtendWith(MockitoExtension.class): @Mock 사용을 위해 설정합니다.

@Mock: 가짜 객체를 주입합니다.

 

 

 

test1은 updateProduct 메서드 전체가 잘 동작하는지 파악하기 위한 것입니다.

User user = new User();
ProductRequestDto requestProductDto = new ProductRequestDto(
        "Apple <b>맥북</b> <b>프로</b> 16형 2021년 <b>M1</b> Max 10코어 실버 (MK1H3KH/A) ",
        "https://shopping-phinf.pstatic.net/main_2941337/29413376619.20220705152340.jpg",
        "https://search.shopping.naver.com/gate.nhn?id=29413376619",
        3515000
);

Product product = new Product(requestProductDto, user);

 

위와 같은 과정이 필요합니다. 실제 서비스 단에서 아래와 같은 코드가 있기 때문입니다.

Product product = productRepository.findById(id).orElseThrow(() -> new NullPointerException("해당 상품을 찾을 수 없습니다."));

 

 

만약 produrct 객체를 만들어주지 않았다면 NullPointerException이 발생합니다.

 

 

또한 아래와 같은 코드를 작성해주어  productRepository.findById(productId) 의 결과값으로 Optional.of(product)이 반환되게 해야합니다. 

given(productRepository.findById(productId)).willReturn(Optional.of(product));

 

테스트에서는 데이터베이스에 접근하지 않지만, 실제 ProductService의 updateProduct 메서드가 productRepository.findById를 호출할 때 특정 Product 객체를 반환받아야만 다음 로직을 이어갈 수 있습니다. 위와 같은 코드를 통해 가상으로 반환하게 만들어 실제 데이터베이스(에 대한 접근) 없이도 테스트가 가능하도록 해줍니다.

 


 


test2에서는 위와 같은 과정이 필요하지 않습니다. 

 

test2는 아래 코드가 잘 동작하는지 확인하는 테스트이고, 아래 코드는 NullPointerException 발생 지점보다 위이기 때문입니다.

 

int myprice = requestDto.getMyprice();
if (myprice < MIN_MY_PRICE) {
throw new IllegalArgumentException("유효하지 않은 관심 가격입니다. 최소 " + MIN_MY_PRICE + " 원 이상으로 설정해 주세요.");
}

 

 


예외처리에 대한 테스트이므로 when과 then에 아래와 같은 코드가 들어갑니다.

// when
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
    productService.updateProduct(productId, requestMyPriceDto);
});

// then
assertEquals(
        "유효하지 않은 관심 가격입니다. 최소 " +ProductService.MIN_MY_PRICE + " 원 이상으로 설정해 주세요.",
        exception.getMessage()
);

'Spring > 팀스파르타' 카테고리의 다른 글

48. OAuth로 카카오 로그인 및 회원 가입  (0) 2024.12.06
47. 소셜 로그인 - OAuth  (0) 2024.12.06
43. 단위 테스트  (0) 2024.12.06
42. 고아 Entity 삭제  (0) 2024.12.06
41. 영속성 전이  (0) 2024.12.06