Spring Boot (5) 테스트 코드

넙데데맨·2022년 7월 22일
0
post-custom-banner

테스트 코드

작성 이유

  • 개발 과정 문제 발견
  • 리팩토링 리스크 줄이기
  • 애플리케이션 가동한 테스트보다 빠른 진행
  • 명세 문서로서의 기능
  • 좋은 코드 생산

단위 테스트

애플리케이션 모듈 독립적 테스트
비용이 적게 들어 피드백하기 쉽다

통합 테스트

애플리케이션을 구헝하는 모듈을 결합해 전체적인 로직이 의도한 대로 동작하는 지 테스트
테스트 비용 커짐

테스트 코드 작성법

Given-When-Then 패턴

Given - 테스트에 필요환 환경 설정
When - 실제 테스트 코드 포함, 결과 가져옴
Then - 결과 검증

F.I.R.S.T 전략

Fast 빠르게
Isolated 목적 대상에 대해서만
Repeatable 반복 가능하게
Self-Validating 테스트의 성공 실패 유무도 검증할 수 있어야함
Timely 애플리케이션 코드 구현 전에 완성

실습

ProductServiceImpl

	import org.slf4j.Logger;
	import org.slf4j.LoggerFactory;
    
    private final Logger LOGGER = (Logger) LoggerFactory.getLogger(ProductServiceImpl.class);
    
	@Override // 읽기 메소드
    public ProductResponseDto getProduct(Long number) {
        LOGGER.info("[getProduct] input number : {}",number);

        Product product = productRepository.findById(number).get();

        LOGGER.info("[getProduct] product number : {}, name : {}", product.getNumber(),product.getName());
        ProductResponseDto productResponseDto = new ProductResponseDto();
        productResponseDto.setNumber(product.getNumber());
        productResponseDto.setName(product.getName());
        productResponseDto.setPrice(product.getPrice());
        productResponseDto.setStock(product.getStock());
        return productResponseDto;
    }

TestLifeCycle

JUnit 생명주기 어노테이션

@Test 테스트 코드 포함 메소드 정의
@BeforeAll 테스트 시작 전 호출 메서드 정의
@BeforeEach 각 테스트 메서드가 실행 전 동작하는 메서드
@AfterAll 테스트 종료 후 호출 메서드 정의
@AfterEach 각 테스트 종료 후 호출되는 메서드 정의

Spring Boot 테스트

1개 이상 DI 받는 코드 테스트

하나의 모듈만 테스트하고자 할 때 외부에서 주입받는 객체는 외부 요인에 해당한다.
외부 요인의 관여 없이 테스트해야 하기 때문에 Mock 객체를 생성해서 해결한다.

테스트 어노테이션

@WebMvcTest 웹에서 사용되는 요청과 응답 테스트 수행
대상 클래스만 로드해 테스트를 수행한다.
@MockBean 가짜 객체를 생성해 주입하는 역할로 Mockito의 given() 메소드를 통해 동작을 정의해야한다
@Test 테스트 코드가 포함됐다고 선언하는 어노테이션 JUnit Jupiter에서 해당 어노테이션을 감지해 테스트 계획에 추가한다.
@DisplayName 테스트 메소드 이름이 복잡해 가독성이 떨어질 경우 테스트 표현 정의를 보기 쉽게 정의할 수 있다.

슬라이스 테스트

  • @WebMvcTest 어노테이션을 사용한 테스트를 슬라이스 테스트라고 하며 단위 / 통합 테스트의 중간 개념이다.
  • 레이어드 아키텍처 기준 각 레이어별로 나눠 테스트 진행한다는 뜻
  • 단위 테스트를 해야하지만 모듈에 사용되는 외부 요인이 있어야 의미 있는 경우 슬라이스 테스트를 한다.

테스트 실습

@WebMvcTest(ProductController.class)
public class ProductController {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    ProductServiceImpl productService;
    
    @Test
    @DisplayName("MockMvc를 통한 Product 데이터 가져오기 테스트")
    void getProductTest() throws Exception{
        given(productService.getProduct(123L)).willReturn(
                new ProductResponseDto(123L,"pen", 5000,2000)
        );

        String productId="123";

        mockMvc.perform(
                get("/product?numebr="+productId)
        ).andExpect(status().isOk())
         .andExpect(jsonPath("$.number").exists())
                .andExpect(jsonPath("$.name").exists())
                .andExpect(jsonPath("$.price").exists())
                .andExpect(jsonPath("$.stock").exists())
                .andDo((print()));
                verify(productService).getProduct(123L);
    }
}

given() 어떤 메소드가 호출 되어 어떤 파라미터 주입 받는 지 가정
willReturn() 어떤 결과를 리턴할 것인지 정의한다.

MockMvc 서블릿 컨테이너 없이 가상의 MVC 환경에서 모의 HTTP 서블릿 요청하는 유틸리티 클래스
perform() 서버로 URL 요청을 보내는 것처럼 테스트 코드를 작성하고 HTTP 요청 정보를 설정해 테스트할 수 있다.
andExpect() 사용해 결괏값 검증 ResultMatcher 활용 생성
jsonPath().exists() 해당 값이 존재하는 지 검증
andDo() 요청 응답 전체 내용 확인
verify 메서드가 실행됐는 지 검증하는 역할로 given()에 정의된 동작과 대응

profile
차근차근
post-custom-banner

0개의 댓글