Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20240902 테스트코드 본문

개발Article

[TIL]20240902 테스트코드

최밤빵 2024. 9. 3. 00:30

결제기능이 테스트가 너무 안되서 포스트맨만 돌려보고있다가, 테스트코드에 대한 설명을 들었다. 작성해본적도 없고, 강의를 듣지도 않아서 잘 모르는 내용이라, 테스트코드에 관한 개발일지를 작성하기로했다. 

오늘 개발일지에서는 결제 기능에 대한 테스트 코드를 작성해 보았다. 결제 기능은 사용자와의 신뢰를 구축하는 중요한 부분이기 때문에, 이를 검증하기 위한 테스트 코드는 좋은방법인 것 같아서! 테스트를 통해 예상치 못한 버그나 결제 처리 실패와 같은 문제를 미리 발견하고, 이를 해결할 수 있다.

▶ 스크린샷을 통한 테스트코드 설명 (with 정재님) 

→ 테스트 코드에서 Mock 객체를 활용하여 테스트에 필요한 가짜 데이터를 주입하는 방법을 보여준다. MockitoAnnotations.openMocks(this)를 통해 Mock 객체가 초기화되고, mock() 메서드를 사용해 특정 객체의 가짜 인스턴스를 생성하여 테스트 코드에서 사용할 수 있다.Mocking을 통해 실제 객체 대신 가짜 객체를 사용해 테스트를 수행할 수 있다.

→  IntelliJ IDEA에서 JUnit 테스트를 실행하는 화면을 보여준다. 테스트 실행 시 커버리지(Coverage)도 함께 확인할 수 있고, 코드의 어느 부분이 테스트되었는지 시각적으로 확인할 수 있어 테스트 범위를 평가하는 데 유용하다. @Test 어노테이션이 붙은 메서드를 개별적으로 실행하거나, 클래스 전체 테스트를 실행할 수 있다.

 

▶결제 기능 테스트 코드 작성

결제 기능을 위한 테스트 코드를 작성하기 위해, 지금 구현중인 결제기능에서 PaymentController PaymentService 클래스에 대한 단위 테스트를 작성했다. 단위 테스트는 코드의 특정 부분(메서드 등)이 예상대로 동작하는지 확인하는 테스트로, 기능의 개별적인 부분을 검증하는 데 유용하다.

 

▶ PaymentController 테스트 코드

 

▽ PaymentController에서 결제 완료와 취소를 처리하는 API에 대한 테스트 코드 예시

→ Spring의 MockMvc를 사용하여 컨트롤러 레이어를 테스트한다.

package com.clean.cleanroom.payment.controller;

import com.clean.cleanroom.payment.dto.PaymentRequestDto;
import com.clean.cleanroom.payment.dto.PaymentResponseDto;
import com.clean.cleanroom.payment.service.PaymentService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.mockito.ArgumentMatchers.any;

@WebMvcTest(PaymentController.class)
class PaymentControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private PaymentService paymentService;

    private PaymentRequestDto paymentRequestDto;
    private PaymentResponseDto paymentResponseDto;

    @BeforeEach
    void setUp() {
        paymentRequestDto = new PaymentRequestDto("test-imp-uid", 1000);
        paymentResponseDto = new PaymentResponseDto("test-imp-uid", 1000, "paid", "https://receipt.url");
    }

    @Test
    void completePayment_shouldReturnSuccess() throws Exception {
        // Mocking PaymentService의 결과
        Mockito.when(paymentService.completePayment(any(PaymentRequestDto.class)))
                .thenReturn(paymentResponseDto);

        // PaymentController의 /complete API 테스트
        mockMvc.perform(post("/api/payments/complete")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("{\"impUid\": \"test-imp-uid\", \"amount\": 1000}"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.impUid").value("test-imp-uid"))
                .andExpect(jsonPath("$.status").value("paid"));
    }
}

 

→ 이 테스트 코드는 PaymentController의 /api/payments/complete 엔드포인트가 정상적으로 작동하는지 확인한다. MockMvc를 사용하여 컨트롤러에 HTTP 요청을 보내고, 서비스 레이어를 모킹(mocking)하여 테스트 환경에서 실제로 API가 호출되도록 한다.

 

▶ PaymentService 테스트 코드

 

▽ PaymentService의 결제 완료 기능을 테스트하기 위한 예시 코드

→ RestTemplatePaymentRepository를 모킹하여 결제 API와 데이터베이스 호출을 모의(mock)하여 테스트한다.

package com.clean.cleanroom.payment.service;

import com.clean.cleanroom.config.PortOneConfig;
import com.clean.cleanroom.payment.dto.PaymentRequestDto;
import com.clean.cleanroom.payment.dto.PaymentResponseDto;
import com.clean.cleanroom.payment.entity.Payment;
import com.clean.cleanroom.payment.repository.PaymentRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import static org.junit.jupiter.api.Assertions.*;
        import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

class PaymentServiceTest {

    @Mock
    private PortOneConfig portOneConfig;

    @Mock
    private RestTemplate restTemplate;

    @Mock
    private PaymentRepository paymentRepository;

    @InjectMocks
    private PaymentService paymentService;

    private PaymentRequestDto paymentRequestDto;
    private PaymentResponseDto paymentResponseDto;

    @BeforeEach
    void setUp() {
        paymentRequestDto = new PaymentRequestDto("test-imp-uid", 1000);
        paymentResponseDto = new PaymentResponseDto("test-imp-uid", 1000, "paid", "https://receipt.url");
    }

    @Test
    void completePayment_shouldSavePaymentAndReturnResponse() {
        // Mocking RestTemplate API 호출 결과
        when(portOneConfig.getApiUrl()).thenReturn("https://api.portone.io");
        when(restTemplate.exchange(anyString(), Mockito.any(), Mockito.any(), Mockito.eq(PaymentResponseDto.class)))
                .thenReturn(new ResponseEntity<>(paymentResponseDto, HttpStatus.OK));

        // Payment 저장 로직 모킹
        when(paymentRepository.save(Mockito.any(Payment.class)))
                .thenReturn(new Payment());

        // 실제 PaymentService 테스트
        PaymentResponseDto result = paymentService.completePayment(paymentRequestDto);

        assertEquals("test-imp-uid", result.getImpUid());
        assertEquals(1000, result.getAmount());
        assertEquals("paid", result.getStatus());
    }
}​

 

→ 이 코드는 PaymentService의 completePayment 메서드를 테스트한다. RestTemplatePaymentRepository를 모킹하여 실제 API 호출이나 DB 연동 없이 테스트를 수행할 수 있다.

 

▶테스트 코드의 장점

→ 코드 품질 향상:

테스트 코드는 기능이 제대로 동작하는지 미리 확인할 수 있어 코드 품질을 높인다. 특히 결제와 같은 중요한 기능에서는 오류가 발생할 경우 치명적인 결과를 초래할 수 있기 때문에, 테스트는 필수적이다.

→ 자동화된 검증:

테스트 코드를 작성하면 CI/CD 파이프라인에서 자동으로 빌드와 테스트가 실행되어, 새로운 변경 사항이 코드에 영향을 미치지 않는지 검증할 수 있다.

→ 디버깅 용이성:

테스트를 통해 발생하는 문제를 초기에 발견하고 해결할 수 있어 디버깅이 용이하다.

→ 리팩토링에 안전:

테스트 코드를 보유하고 있으면, 리팩토링을 하더라도 기존 기능이 제대로 동작하는지 쉽게 확인할 수 있다.

 

▶ 정리

오늘은 결제 기능을 위한 테스트 코드를 작성하고(GPT가...!), 테스트 코드가 어떤 장점을 제공하는지 알아보았다. 컨트롤러와 서비스 레이어에 대한 테스트 코드를 통해 각 기능이 제대로 동작하는지 검증할 수 있고, 이를 통해 코드 품질을 향상시키고 버그를 조기에 발견할 수 있다.