Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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
관리 메뉴

seaking110 님의 블로그

테스트 코드 remind 본문

Today I Learned

테스트 코드 remind

seaking110 2025. 3. 10. 13:58

테스트 코드

  • unit 테스트 (단위 테스트) : 도메인 모델과 비즈니스 로직을 테스트, 작은 단위의 코드 및 알고리즘 테스트
  • intergration 테스트 (통합 테스트) : 코드의 주요 흐름들을 통합적으로 테스트
  • e2e 테스트 : 최종 사용자의 흐름에 대한 테스트이며 외부로부터의 요청부터 응답까지 기능이 잘 동작하는지에 대한 테스트

TDD (테스트 주도 개발)

  • 테스트를 먼저 작성한 후 이를 만족하는 코드를 작성하고 리팩토링 하는 방식
  • 진행 과정
    • 원하는 기능을 먼저 테스트 코드로 작성 (무조건 실패)
    • 해당 테스트를 통과하도록 실제 코드를 작성
    • 코드 정리 후 다시 테스트를 실행하여 성공 여부 확인
  • TDD의 한계
    • TDD를 적용한다고 버그가 사라지는 것은 아님
    • 개발 속도를 저하시킬 수 있기 때문에 상황에 따라 적절히 활용

 

좋은 테스트의 F.I.R.S.T 원칙

  • FAST : 단위 테스트는 빠르게 수행
  • Independent (독립적) : 각 테스트는 서로 간섭 없이 독립적으로 실행되어야 한다.
  • Repeatable (반복 가능) : 동일한 입력으로 항상 같은 결과 보장
  • Self-validating (자동 검증) : 테스트는 자동으로 성공/실패를 판별
  • Timely (적시성) : 단위테스트는 실제 코드보다 먼저 작성하는 것이 이상적(TDD의 경우)

 

Given-When-Then

  • given - when - then만 잘 지켜도 훌륭한 테스트 코드

 

JUnit + Mockito

  • junit : 자바에서 가장 많이 사용하는 테스트 프레임워크
  • Mockito : 가짜 객체를 사용하여 의존성을 줄이고 특정 상황을 시뮬레이션 할 수 있는 프레임 워크
  • @Mock : Mock 객체 생성
  • @Spy : 원본 객체를 유지하면서 일부만

 

 

효과적인 단위 테스트 작성법

  • 유지 보수하기 쉬운 테스트
  • 좋은 테스트의 특징
    • 관련 없는 코드 변경으로 인한 실패 x
  • 좋지 않은 테스트의 특징
    • 검증 대상과 관련 없는 변경 때문에 실패하는 테스트
    • 불명확한 테스트 : 뭐가 잘못되어 실패했는지 보완 사항을 알지 어려운 테스트
  • 테스트 유지보수성 향상 방법
    • 내부 구현이 아니라 결과를 검증하는 방식으로 작성
    • 행위 중심 테스트를 지향
    • 명확한 테스트 명명법 사용
    • 테스트 코드에서 복잡한 논리는 지양
  • Dly 원칙 보다 DAMP 원칙이 훨씬 더 중요
  • 이해하기 쉬운 테스트 코드가 훨씬 더 중요하다!

 

안좋은 테스트 코드 예방하기

  • 한 번 작성한 후로 대상 시스템의 요구사항이 바뀌지 않는 한 절대 수정할 일이 없어야 함
  • 순수 리팩터링(외부 인터페이스는 놔두고 내부만 리팩터링 하는 경우) -> 테스트는 변경되지 않아야함
  • 새로운 기능 추가 -> 테스트는 변경되지 않아야함 & 새로운 테스트 추가
  • 버그 수정 -> 기존 테스트 변경 X & 누락된 테스트 추가
  • 행위 추가 -> 기존 테스트 변경 O
  • 과정이 아닌 결과에 초점을 맞춤

 

 

통합 테스트 개념

  • 탄위 테스트만으로는 시스템이 전체적으로 동작하는지 확인 x
  • 데이터베이스, 캐시(Redis), API 연동 등 외부 시스템과의 통합을 검증하는 테스트가 필요
  • Postman을 이용한 API 테스트와 유사
User,UserRepository,UserService,UserController가 있다고 가정

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@Transactional
public class UserControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private UserRepository userRepository;

    @Test
    public void shouldCreateUserAndRetrieveIt() throws Exception {
        // Given - 사용자 데이터
        String userJson = "{\"name\":\"John Doe\", \"email\":\"john@example.com\"}";

        // When - POST 요청으로 사용자 생성
        mockMvc.perform(post("/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(userJson))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value("John Doe"))
                .andExpect(jsonPath("$.email").value("john@example.com"));

        // Then - GET 요청으로 방금 생성한 사용자 조회
        mockMvc.perform(get("/users/john@example.com"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value("John Doe"))
                .andExpect(jsonPath("$.email").value("john@example.com"));
    }

    @Test
    public void shouldReturn404ForNonExistentUser() throws Exception {
        // When - 존재하지 않는 이메일 조회
        mockMvc.perform(get("/users/nonexistent@example.com"))
                .andExpect(status().isNotFound());
    }
}

실행 결과
-성공
[ 2025-03-09 12:34:56 ]  INFO  
UserControllerIntegrationTest: shouldCreateUserAndRetrieveIt() PASSED
[ 2025-03-09 12:34:57 ]  INFO  
UserControllerIntegrationTest: shouldReturn404ForNonExistentUser() PASSED

-실패
[ 2025-03-09 12:34:58 ]  ERROR  UserControllerIntegrationTest: shouldReturn404ForNonExistentUser() FAILED
Expected: HTTP 404
Actual: HTTP 200

 

  • @SpringBootTest
    • Spring Boot 애플리케이션 컨텍스트 전체를 로드하여 실제 환경과 유사하게 테스트할 수 있도록 함.
    • webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT 설정 시, 내장 웹 서버를 랜덤 포트에서 실행하여 통합 테스트 수행.
  • @Transactional
    • 테스트 종료 후 데이터베이스를 자동 롤백하여 데이터가 남지 않도록 방지.
  • MockMvc
    • HTTP 요청을 모의(mock)하여 실제 서버 없이 컨트롤러를 테스트할 수 있도록 지원.
    • mockMvc.perform(post(“/users”)...)을 사용하여 실제 HTTP 요청을 보내는 것처럼 동작.
  • jsonPath("$.name").value("John Doe")
    • JSON 응답에서 특정 필드 값을 검증.
  • status().isOk() / status().isNotFound()
    • HTTP 응답 상태 코드가 기대한 값과 일치하는지 확인.

 

 

가독성을 올리기 위한 전략 - 공유 데이터 셋팅

  • 공유 데이터를 미리 세팅
  • 장점
    • 모든 테스트에서 기능을 확인하기 위해 세팅해줘야 하는 파라미터 값, 변수를 매번 호출 필요 x
    • 이후 엔티티나 어떤 값이 변경되더라도 공유 데이터만 변경하면 됨
  • public final static String TEST_EMAIL1 = "TEST@NAVER.com" 으로 미리 지정
  • User user = TEST_USER!1 이런식으로 미리 지정한 공유 데이터를 활용하여 가독성 높은 테스트 코드 작성 가능

'Today I Learned' 카테고리의 다른 글

Spring Security  (0) 2025.03.12
연관관계와 N+1  (0) 2025.03.11
헷갈리는 개념 정리  (2) 2025.03.05
트러블 슈팅  (0) 2025.02.27
프로젝트에 부족한 부분을 찾아 리팩토링  (0) 2025.02.27