백엔드/Spring

Service 계층 단위 테스트를 위한 Mockito 사용하기

happy_life 2023. 10. 17. 16:36

기존에 @SpringbootTest 어노테이션를 사용하여 Service 계층을 테스트하였습니다. 그러나 실제 DB와 관련된 Repository 계층 등을 직접 주입받아야 했고, 이렇게 된다면 service 계층만의 단위 테스트를 제대로 하지 못할 것이라는 판단을 하였습니다. 따라서 Mockito를 사용하여 리팩토링하였는데, 이 과정을 포스팅하려 합니다.

 

 

Service 계층 단위 테스트 - 기존의 코드

@SpringBootTest
@Transactional(readOnly = true)
@ExtendWith(MockitoExtension.class)
class TicketInformationServiceTest {

    @Autowired
    private TicketInformationRepository ticketInformationRepository;

    @Autowired
    private SubPaymentRepository subPaymentRepository;

    @Autowired
    private PaymentCancelRuleRepository paymentCancelRuleRepository;

    @Autowired
    private AgreementRepository agreementRepository;

    @Test
    @Transactional
    void saveTicketInformation() {
        // given
        TicketInformation ticketInformation = TicketInformation.builder()
                .ticketName("ticketName")
                .ticketSubName("ticketSubName")
                .ticketCount(4)
                .ticketPrice(1000)
                .build();

        Agreement agreement = Agreement.builder()
                .privacyToThirdParty(1)
                .stayRule(1)
                .conciergeRule(1)
                .ageOver14(1)
                .build();

        PaymentCancelRule paymentCancelRule = PaymentCancelRule.builder()
                .content("asd")
                .build();
        paymentCancelRuleRepository.save(paymentCancelRule);

        agreementRepository.save(agreement);

        SubPayment subPayment = SubPayment
                .builder()
                .agreement(agreement)
                .paymentCancelRule(paymentCancelRule)
                .build();

        subPaymentRepository.save(subPayment);

        ticketInformation.setSubPayment(subPayment);
        ticketInformationRepository.save(ticketInformation);


        //when
        TicketInformation foundTicketInformation = ticketInformationRepository.findById(ticketInformation.getId())
                .orElse(null);

        //then
        assertEquals("ticketName",foundTicketInformation.getTicketName());
        assertEquals("ticketSubName", foundTicketInformation.getTicketSubName());
        assertEquals(4, foundTicketInformation.getTicketCount());
        assertEquals(1000, foundTicketInformation.getTicketPrice());
        assertEquals(subPayment, foundTicketInformation.getSubPayment());

        assertEquals(foundTicketInformation, subPayment.getTicketInformationList().get(0));
    }

}

 

test 코드에서 보듯 @Autowired, @SpringbootTest 등을 통해 실제로 DB에 접근하여 테스트를 진행하였습니다.

 

 

 

Service 계층 단위 테스트 - Mockito 코드

@ExtendWith(MockitoExtension.class)
class TicketInformationServiceTest {

    @Mock
    TicketInformationRepository ticketInformationRepository;

    @Mock
    SubPayment subPayment;

    @InjectMocks
    TicketInformationService ticketInformationService;

    @Test
    void saveTicketInformation() {
        // given
        PaymentTicketRequestDto paymentTicketRequestDto = new PaymentTicketRequestDto();
        paymentTicketRequestDto.setTicketName("ticketName");
        paymentTicketRequestDto.setTicketSubName("ticketSubName");
        paymentTicketRequestDto.setTicketCount(4);
        paymentTicketRequestDto.setTicketPrice(1000);


        TicketInformation ticketInformation = TicketInformation.builder()
                .ticketName("ticketName")
                .ticketSubName("ticketSubName")
                .ticketCount(4)
                .ticketPrice(1000)
                .build();

        ticketInformation.setSubPayment(subPayment);

        given(ticketInformationRepository.save(any(TicketInformation.class))).willReturn(ticketInformation);

        // when
        TicketInformation savedTicketInformation = ticketInformationService.saveTicketInformation(subPayment, paymentTicketRequestDto);

        // then
        assertEquals("ticketName",savedTicketInformation.getTicketName());
        assertEquals("ticketSubName", savedTicketInformation.getTicketSubName());
        assertEquals(4, savedTicketInformation.getTicketCount());
        assertEquals(1000, savedTicketInformation.getTicketPrice());
        assertEquals(subPayment, savedTicketInformation.getSubPayment());
    }

}

 

1. @ExtendWith(MockitoExtension.class)

Mockito와 Junit5를 사용할 때 Spring에 의존하지 않고 test하는 어노테이션

 

2. @InjectMocks

Mock 객체를 @InjectMocks 어노테이션이 붙은 객체에 의존성 주입을 해주는 어노테이션

 

3. @Mock

가짜 객체

 

 

 

장점

1. 실제 Spring 서버를 실행하고 DB에 연결하는 과정에서 소요되는 시간을 절감할 수 있습니다.

2. Mock을 사용하여 원하는 계층에 관련된 독립적인 테스트를 진행할 수 있습니다.

 

 

참고

https://stackoverflow.com/questions/61433806/junit-5-with-spring-boot-when-to-use-extendwith-spring-or-mockito