Spring - 통합테스트와 단위테스트의 비교와 Stubbing 방식의 차이

salgu·2023년 10월 18일
0

Spring

목록 보기
21/22
post-thumbnail

Spring Test, 통합테스트


@SpringBootTest애플리케이션 전체를 테스트하는 데 사용되며, @DataJpaTestJPA 관련 빈들을 중점으로 테스트하고, @WebMvcTest웹 계층을 중점으로 테스트합니다. 이러한 애노테이션을 사용하여 필요한 범위와 목적에 맞게 테스트를 구성할 수 있습니다.

@SpringBootTest

@SpringBootTest는 스프링 애플리케이션 컨텍스트를 로드하는 테스트 환경을 설정합니다.
이 애노테이션을 사용하면 전체 스프링 애플리케이션 컨텍스트를 시작하므로, 모든 빈과 구성 요소가 스프링 컨테이너에 띄워지게됩니다.
주로 통합 테스트와 엔드 투 엔드 테스트에서 사용됩니다.
테스트 범위가 넓어서, 실제 애플리케이션과 유사한 환경에서 테스트를 수행하고 실제 데이터베이스 연결 및 외부 서비스와 상호 작용이 가능합니다.

@DataJpaTest

@DataJpaTestJPA 관련 빈들만 스프링 컨테이너에 띄워지게됩니다.
이 애노테이션을 사용하면 JPA 리포지터리, 엔티티, 데이터 소스와 관련된 빈들만 로드되므로, 데이터 액세스 레이어의 테스트에 유용합니다.
내장 데이터베이스(H2 등)를 사용하여 테스트 데이터베이스를 제공하고, 테스트 범위가 좀 더 좁아서 테스트를 더 빠르게 실행할 수 있습니다.

@WebMvcTest(controllers = {Test할 Controller.class})

@WebMvcTest는 웹 애플리케이션의 웹 계층 테스트를 위한 환경을 설정합니다.
이 애노테이션을 사용하면 Controller, ControllerAdvice, 뷰 컴포넌트 등과 관련된 빈들만 스프링 컨테이너에 띄워지게됩니다.
웹 요청 및 응답을 시뮬레이션하여 웹 애플리케이션의 행위를 테스트할 때 사용됩니다.
실제 HTTP 요청을 보내지 않고도 컨트롤러 동작을 테스트할 수 있습니다.



순수 Mockito, 단위 테스트


@ExtendWith(MockitoExtension.class)

순수 Mockito 테스트는 주로 유닛 테스트 또는 모듈 테스트로 사용됩니다. 이러한 테스트에서는 주로 특정 클래스 또는 메소드를 격리하여 테스트하고, 외부 의존성을 모킹하여 테스트를 진행합니다.



Spring Test, 순수 Mockito 차이점


순수 Mockito는 특정 클래스 또는 메소드의 유닛 테스트를 지원하는 데 사용되고, 스프링 테스트 애노테이션을 통한 통합테스트는 스프링 애플리케이션의 다양한 레이어 또는 컴포넌트 간의 통합 및 기능적인 테스트를 지원하는 데 사용됩니다.



Stubbing


Spring Mockito Stubbing 방식

@SpringBootTest	// Spring 통합테스트
class PaymentServiceTest {

	@Autowired
    PaymentService paymentService;
    
    @MockBean	// Bean객체를 Mocking
    MailService mailService;
    
    @DisplayName("결제한다.")
    @Test
    void payment() {
    	// given
        Mockito.when(mailService.sendEmail(any(String.class)))
                .thenReturn(true);
                
        // when
    	Payment payment = paymentService.payment();
        
        // then
        ~검증~
    }
}

해당 코드의 전제 조건은 PaymentService(Bean)가 MailService(Bean)를 주입받아 사용하는 구조이고 payment() 메소드 내부에서 MailService의 sendEmail()을 호출하는 전제입니다.

@MockBean을 사용하게 되면 해당 MailService객체가 Mock객체로 PaymentService에 의존성이 주입되게 됩니다.

Mockito 라이브러리의 when()과 then() 메소드를 활용하여 특정파라미터의 반환값을 지정해줍니다.

순수 Mockito Stubbing 방식

Spring context에 등록되지 않은 객체를 테스트 할때는 해당 방식을 사용하거나 스프링을 사용하지 않고 단위테스트할때 활용하면 됩니다.

순수 Mockito를 Stubbing하는 방식에는 크게 두가지가 있습니다.
1. 직접 코드로 Mockito.mock() 메소드를 활용하여 직접 Mock객체를 생성해주는 방식
2. 간편하게 어노테이션으로 생성하는 방식

1. 직접 Mock객체를 생성하는 방식

@ExtendWith(MockitoExtension.class)	// Mockito를 사용하겠다는 어노테이션
class PaymentServiceTest {
    
    @DisplayName("결제한다.")
    @Test
    void payment() {
    	// given
        MailService mailService = Mockito.mock(MailService.class);
        Mockito.when(mailService.sendEmail(any(String.class)))
                .thenReturn(true);
        
        PaymentService paymentService = new PaymentService(mailService);
        
        // when
    	Payment payment = paymentService.payment();
        
        // then
        ~검증~
    }
}

2. 어노테이션으로 Mock객체를 생성하고 주입하는 방식

@ExtendWith(MockitoExtension.class)	// Mockito를 사용하겠다는 어노테이션
class PaymentServiceTest {

    @Mock	// Mocking할 객체 지정
    MailService mailService;

    @InjectMocks	// Mocking된 객체를 주입받을 곳 지정
    PaymentService paymentService;
    
    @DisplayName("결제한다.")
    @Test
    void payment() {
    	// given
        Mockito.when(mailService.sendEmail(any(String.class)))
                .thenReturn(true);
        
        // when
    	Payment payment = paymentService.payment();
        
        // then
        ~검증~
    }
}

2번 방식을 사용하는것이 반복되는 코드를 줄일 수 있고 테스트 로직에 집중할 수 있습니다.

BDDMockito

일반적으로 given, when, then을 사용하는 BDD 스타일로 테스트를 작성합니다.
그렇게 작성하다보면 given절에서 stubbing을 할때 어색한점을 볼 수 있습니다.

    @DisplayName("결제한다.")
    @Test
    void payment() {
    	// given
        Mockito.when(mailService.sendEmail(any(String.class)))
                .thenReturn(true);
        
        // when
    	Payment payment = paymentService.payment();
        
        // then
        ~검증~
    }

given절에서 when을 사용하며 stubbing을 합니다.
이걸 개선하기 위해 나온것이 BDDMockito입니다.

    @DisplayName("결제한다.")
    @Test
    void payment() {
    	// given
        // 기존 문법
//        Mockito.when(mailService.sendEmail(any(String.class)))
//                .thenReturn(true);
        
        // 변경된 문법
        BDDMockito.given(mailService.sendEmail(any(String.class)))
                .willReturn(true);
        
        // when
    	Payment payment = paymentService.payment();
        
        // then
        ~검증~
    }

BDDMockito의 given을 사용하여 stubbing을 한다면 좀더 BDD스타일에 맞게 역할을 제대로 분리하여 작업을 할 수 있습니다.
이름만 다르지 내부적인 기능은 같습니다.

profile
https://github.com/leeeesanggyu, leeeesanggyu@gmail.com

0개의 댓글