[Spring] 서비스 객체의 테스트

WOOK JONG KIM·2022년 10월 30일
0
post-thumbnail

아무 의존성을 주입받지 않은 상태에서 단위 테스트를 작성하였을 때

public class ProductServiceTest {

    private ProductRepository productRepository = Mockito.mock(ProductRepository.class);
    private ProductServiceImpl productService;

    @BeforeEach
    public void setUpTest(){
        productService = new ProductServiceImpl(productRepository);
    }

    @Test
    void getProductTest(){
        Product givenProduct = new Product();
        givenProduct.setNumber(123L);
        givenProduct.setName("펜");
        givenProduct.setPrice(1000);
        givenProduct.setStock(1234);

        Mockito.when(productRepository.findById(123L))
                .thenReturn(Optional.of(givenProduct));

        ProductResponseDto productResponseDto = productService.getProduct(123L);

        Assertions.assertEquals(productResponseDto.getNumber(), givenProduct.getNumber());
        Assertions.assertEquals(productResponseDto.getName(), givenProduct.getName());
        Assertions.assertEquals(productResponseDto.getPrice(), givenProduct.getPrice());
        Assertions.assertEquals(productResponseDto.getStock(), givenProduct.getStock());

        verify(productRepository).findById(123L);

    }
}
  1. Mockito의 mock()메서드를 통해 Mock객체로 ProductRepository를 주입받음
  2. 각 테스트 전에 ProductService 객체를 초기화하여 사용
  3. Given-When-Then 패턴에서 Given 구문에서는 테스트에 사용될 Product 엔티티 생성
  4. getProduct 메서드를 호출하여 동작 테스트
  5. 리턴 받은 객체에 대해 Assertion을 통해 값을 검증
  6. 검증 보완을 위해 42번 줄과 같이 verify() 메서드에 대한 테스트 코드 작성
@Test
    void saveProductTest(){
        Mockito.when(productRepository.save(any(Product.class)))
                .then(returnsFirstArg());
        
        ProductResponseDto productResponseDto = productService.saveProduct(
                new ProductDto("펜", 1000, 1234));
        
        Assertions.assertEquals(productResponseDto.getName(), "펜");
        Assertions.assertEquals(productResponseDto.getPrice(), 1000);
        Assertions.assertEquals(productResponseDto.getStock(), 1234);
        
        verify(productRepository).save(any());
    }

any()는 Mockito의 ArgumentMatchers에서 제공하는 메서드로 Mock 객체의 동작을 정의하거나 검증하는 단계에서 조건으로 특정 매개변수의 전달을 설정하지 않고 메서드의 실행만을 확인하거나 좀 더 큰 범위의 클래스 객체를 매개변수로 전달받는 등의 상황에 사용

일반적으로 given()으로 정의된 Mock 객체의 메서드 동작 감지는 매개변수의 비교를 통해 이뤄지나, 레퍼런스 변수의 비교는 주솟값으로 이뤄지기 때문에 any()를 사용해 클래스만 정의하는 경우도 있음

Mock객체를 직접 생성하지 않고 @MockBean 어노테이션을 사용해 스프링 컨테이너에 Mock객체를 주입받는 방법

@ExtendsWith(SpringExtension.class)
@Import({ProductServiceImpl.class})
class ProductServiceTest2{

	@MockBean
    ProductRepository productRepository
    
    @Autowired
    ProductService productService
    
    테스트 코드 생략
}

이 전에는 Mockito를 통해 리포지토리를 Mock객체로 대체하는 작업을 수행하고 서비스 객체를 직접 초기화하였음

반면 위에서는 Test 어노테이션을 통해 Mock 객체를 생성하고 의존성을 주입받고 있음

둘의 차이 -> 스프링의 기능에 의존을 하는지 안하는지

두예제 다 Mock객체를 활용한 테스트 방식인 것은 동일하나 @MockBean을 사용하는 방식은 스프링에 Mock객체를 직접 주입받는 방법이며 Mockito.mock()을 사용하는 방식은 스프링 빈에 등록하지 않고 직접 객체를 초기화해서 사용하는 방식

보통은 mock객체를 직접 생성하는 것이 더 빠름

스프링에서 객체를 주입받기 위해 ExtendsWith(SpringExtension.class)를 사용해 JUnit5의 테스트에서 스프링 테스트 컨텍스트를 사용하도록 설정

이후 @Autowired 어노테이션으로 주입받는 ProductService를 주입받기 위해 직접 클래스를 @Import 어노테이션을 사용

profile
Journey for Backend Developer

0개의 댓글