상품 관리 애플리케이션에 객체지향 더하기 (3)

sookyoung.k·2024년 6월 18일
0

☕Java

목록 보기
10/11
post-thumbnail

🦉 모킹과 단위 테스트

앞서 작성했던 테스트는 일명 통합 테스트(Integration Test)라고 한다.
👉🏻 2개 이상의 클래스(혹은 컴포넌트)가 협력하는 테스트, 일반적으로 스프링 부트 애플리케이션을 실행시켜서 두 클래스에 대한 빈을 실제로 생성하여 테스트한다.

→ SimpleProductService를 테스트하기 위해서 ProductRepository 인터페이스의 구현체 중 하나를 Profile에 따라 빈을 생성하여 SimpleProductService를 테스트하는 데 사용
👉🏻 '내가 개발 중인 코드' 외에도 '내가 개발 중인 코드가 의존하고 있는 코드'까지 대상으로 실행한다

⚠️ 이를 위해서는 ProductRepository에 대한 구현체가 반드시 필요하다는 문제가 생긴다. 실무에서는 특정 인터페이스에 대한 구현체가 없는 상태에서 개발하고 있는 로직을 테스트해야 하는 경우가 종종 발생한다.

🪶 단위 테스트

다른 클래스를 사용하지 않고 작동하는 테스트

→ 개발 중인 로직만 간결하고 빠르게 테스트하기 위해 실행한다.
👉🏻 이럴 때 사용할 수 있는 기법이 바로 '모킹(Mocking)'이다!

🪶 모킹

자바에서는 모킹 라이브러리로 'Mockito'를 많이 사용한다.

Mockito로 단위 테스트 코드 작성하기

SimpleProductServiceUnitTest.java

package kr.co.hanbit.product.management.application;

import kr.co.hanbit.product.management.domain.Product;
import kr.co.hanbit.product.management.domain.ProductRepository;
import kr.co.hanbit.product.management.presentation.ProductDto;
import org.junit.jupiter.api.DisplayName;
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 static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class SimpleProductServiceUnitTest {

    @Mock
    private ProductRepository productRepository;

    @Mock
    private ValidationService validationService;

    @InjectMocks
    private SimpleProductService simpleProductService;

    @Test
    @DisplayName("상품 추가 후에는 추가된 상품이 반환되어야한다.")
    void productAddTest() {
        ProductDto productDto = new ProductDto("연필", 300, 200);
        Long PRODUCT_ID = 1L;

        Product product =ProductDto.toEntity(productDto);
        product.setId(PRODUCT_ID);
        when(productRepository.add(any())).thenReturn(product);

        ProductDto savedProductDto = simpleProductService.add(productDto);

        assertTrue(savedProductDto.getId().equals(PRODUCT_ID));
        assertTrue(savedProductDto.getName().equals(productDto.getName()));
        assertTrue(savedProductDto.getPrice().equals(productDto.getPrice()));
        assertTrue(savedProductDto.getAmount().equals(productDto.getAmount()));
    }

}

새로운 테스트 코드 파일을 추가한다. (기존 테스트 코드와 동일한 패키지)

모킹된 테스트 코드의 동작 원리

  • @ExtendWith(MockitoExtension.class) → 스프링 부트 애플리케이션을 실행시키지 않고도 테스트 코드를 실행시킬 수 있다
    • @SpringBootTest와 차이: 스프링 부트 애플리케이션이 뜨느냐 마느냐를 결정
    • @SpringBootTest → 스프링 부트 애플리케이션이 시작되면서 필요한 의존성들을 빈으로 등록하고 주입하는 과정을 거친다
  • @ActiveProfiles 지정 X → ProductRepository 구현체 없이 테스트 할 수 있는 단위 테스트를 할 것이기 때문에 필요가 없다
  • @Mock → 해당 의존성에 '목 객체(Mock Object)'를 주입한다는 의미로 테스트 코드에서 목 객체가 어떤 메서드에 대해 어떤 동작을 할지는 스스로 정의해야 함
  • @InjectMock → 위에서 @Mock으로 주입해 준 목 객체들을 SimpleProductService 내에 있는 의존성에 주입해 주는 역할, 목 객체가 아니라 실제 인스턴스를 생성하여 로직으로 사용할 수 있다
  • when(productRepository.add(any())).thenReturn(product); → 목 객체가 어떻게 행동해야 하는지 정의
    • when: 목 객체가 when에 해당하는 동작을 수행할 때
      any(): 아무 값이나 들어간다는 의미
    • thenReturn: thenReturn에 있는 값을 반환한다
profile
영차영차 😎

0개의 댓글