Study/SpringBoot

Spring Boot 테스트 코드: 통합 테스트 vs 단위 테스트

kanado 2025. 3. 27. 15:42

통합 테스트는 전체 애플리케이션 컨텍스트를 로드하여 실제 운영 환경과 유사한 조건에서 컴포넌트 간의 상호작용과 전체 시스템의 동작을 검증하는 데 목적이 있으며, 주로 @SpringBootTest, @AutoConfigureMockMvc과 MockMvc를 활용해 테스트한다. 반면, 단위 테스트는 특정 클래스나 메서드를 외부 의존성과 격리된 상태에서 검증하여 기능의 정확성을 빠르고 효율적으로 확인하는 데 초점을 두며, @Mock, @InjectMocks, Mockito 등을 사용해 최소한의 환경에서 테스트를 수행한다.

1️⃣ 통합 테스트

전체 Spring Boot 애플리케이션 컨텍스트(ioc)를 로드하여, 실제 환경과 유사한 조건에서 컨트롤러의 동작을 검증하는 테스트다. (DB, 서비스, 리포지토리, 시큐리티 등 모든 설정을 포함한 환경에서 테스트)

그 목적은 모듈 간 인터페이스, 데이터 흐름, 그리고 외부 시스템과의 연동이 제대로 작동하는지를 확인하는 데 있다. MockMvc를 사용해 HTTP 요청을 모의로 보내고, 그에 대한 응답을 검증합니다.

✅ 주요 어노테이션

  1. @SpringBootTest
    • Spring Boot 애플리케이션 전체 컨텍스트를 로드해서 테스트함.
    • 실제 운영 환경과 유사하게 모든 Bean을 등록하고 테스트 가능.
  2. @AutoConfigureMockMvc
    • @SpringBootTest와 함께 사용.
    • MockMvc를 자동으로 설정해줌.
    • 실제 웹 서버를 띄우지 않고도 HTTP 요청을 시뮬레이션 가능.
  3. @Transactional
    • 테스트 메서드 실행 후 DB 롤백을 자동으로 처리함.
    • 테스트 간 데이터 영향 방지에 유용.
  4. @TestConfiguration
    • 테스트 전용 Bean을 설정할 때 사용.
    • 운영 환경과는 다른 테스트용 설정이 필요할 때 활용.
  5. @ActiveProfiles("test")
    • 테스트 실행 시 사용할 프로파일 지정.
    • 예: application-test.yml을 로드하고 싶을 때 → 운영에서는 실제 DB, 보안 설정, API 키 등을 사용하지만 테스트에서는 가짜 DB, 간단한 보안 설정, mock 서버 등을 쓰고 싶을 때 사용한다.

👉 사용 예시

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
class MemberControllerTest {

    @Autowired
    private MockMvc mockMvc; ⭐️

    @Autowired
    private ObjectMapper objectMapper; ⭐️

    @Autowired
    private JdbcMemberRepository jdbcMemberRepository;

    private final String email = "test@example.com";
    private final String password = "123456";
    private final String nickname = "tester";

    @BeforeEach
    void setUp() {
        jdbcMemberRepository.save(new Member(email, password, nickname));
    }

    @Test
    void signInSuccessTest() throws Exception {
        SignInRequestDTO request = new SignInRequestDTO(email, password);
        mockMvc.perform(post("/auth") // MockMvc를 사용해서 가짜 HTTP 요청을 보냄.
                        .contentType(MediaType.APPLICATION_JSON) // JSON 데이터를 보내는 요청임을 서버에 알림
                        .content(objectMapper.writeValueAsString(request))) // 실제 HTTP 요청의 본문(body)을 설정하는 메서드
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value("SU")) // JSON에서 code 키를 가리키는 JSONPath 표현식
                .andExpect(jsonPath("$.user_id").isNumber());
    }
}

⭐️ MockMvc란?

MockMvc는 Spring MVC의 HTTP 요청/응답 과정을 서버를 실제로 띄우지 않고 시뮬레이션할 수 있게 도와주는 테스트 도구

즉, 실제 웹 서버 없이도 →

  • 컨트롤러에 HTTP 요청을 보내고
  • 응답을 받아서
  • 상태 코드, JSON 값, 헤더 등을 검증할 수 있다.

⭐️ ObjectMapper란?
ObjectMapper는 Jackson이라는 JSON 라이브러리의 핵심 클래스 중 하나로, 자바 객체와 JSON 문자열 간 변환을 도와주는 도구
MockMvc를 사용할 때, HTTP 요청에 JSON 바디를 담아 전송하려면 @RequestBody와 호환되도록 자바 객체를 JSON 문자열로 변환해야 하는데, 이때 ObjectMapper가 필요하다.

 

2️⃣ 단위 테스트

단위 테스트는 애플리케이션의 구성 요소 중 가장 작은 단위인 하나의 클래스 또는 메서드 단위의 로직을 외부 의존성과 격리된 상태에서 검증하는 테스트다. 이 테스트는 일반적으로 Service, 도메인 로직, 유틸리티 클래스 등에 대해 수행되며, 외부 시스템(DB, 웹 요청 등)과의 연동 없이 빠르고 정확하게 비즈니스 로직의 동작을 검증하는 것이 목적이다.

✅ 주요 어노테이션

  1. @Mock
    • 가짜(Mock) 객체 생성
    • 테스트 대상의 의존 객체를 모킹할 때
    • 원하는 동작을 하도록 설정할 수 있는 테스트용 가짜 객체
  2. @InjectMocks: Mock 객체를 주입받는 실제 테스트 대상
  3. @ExtendWith(MockitoExtension.class): JUnit 5(Jupiter) 기반 테스트에서 Mockito 기능(@Mock, @InjectMocks 등)을 활성화

👉 사용 예시

@ExtendWith(MockitoExtension.class)
class MemberServiceTest {
    private final int userId = 1;
    private final String rawEmail = "test@email.com";
    private final String rawPassword = "password123";
    private final String rawNickname = "nickname";
    private final String rawImage = "profile.jpg";
    private final String savedPassword = "password123";

    @Mock
    private JdbcMemberRepository jdbcMemberRepository;

    @InjectMocks
    private MemberService memberService;

    @Test
    @DisplayName("회원가입 실패 - 중복 이메일")
    void registerMember_shouldReturnDuplicateEmail_whenEmailExists() {
        // given
        SignUpRequestDTO request = new SignUpRequestDTO(rawEmail, rawPassword, rawNickname, null);
        when(jdbcMemberRepository.findByEmail(request.getEmail()))
                .thenReturn(Optional.of(new Member(rawEmail, rawPassword, rawNickname)));
        // DB를 조회하지 않고, 직접 만든 Member 객체가 리턴되게 Mock하는 코드

        // when
        ResponseEntity<?> response = memberService.registerMember(request);
        ApiResponse body = (ApiResponse) response.getBody();

        // then
        assertEquals(ResponseCode.DUPLICATE_EMAIL, body.getCode());
        verify(jdbcMemberRepository, never()).save(any());
        // 실제 저장이 일어나지 않았는지 확인
    }
}

3️⃣ 공통 주요 어노테이션

  1. @Test: 테스트 메서드를 나타냄
  2. @DisplayName: 테스트 이름을 보기 좋게 지정 (테스트 리포트 가독성 향상)
  3. @BeforeEach, @AfterEach, @BeforeAll, @AfterAll: 테스트 클래스의 라이프 사이클을 관리하는 어노테이션
  4. @Nested: 내부 테스트 클래스를 계층적으로 표현 (테스트 그룹화)
  5. @ParameterizedTest: 매개변수를 바꿔가며 반복 테스트
  6. @ValueSource, @CsvSource: @ParameterizedTest와 함께 사용하여 다양한 인자 주입

 

끝.