스프링 테스트의 모든 것 - 단위 테스트부터 예외 처리까지

이상민·2024년 9월 9일

본격적으로 스프링 테스트 코드에 관해 공부하기 전에 그에 관한 내용들을 정리해보는 시간을 가져보았다.

스프링 애플리케이션을 개발하다 보면, 테스트는 코드 품질을 유지하고 애플리케이션의 안정성을 보장하는 필수적인 과정입니다. 이 글에서는 스프링에서 자주 사용되는 테스트 방법들과 AOP, 예외 처리 등의 기술을 다루며, 각 기술을 효과적으로 활용할 수 있는 팁과 주의할 점을 정리해보았다.

1. 스프링 단위 테스트 (Spring Unit Testing)

단위 테스트란?

단위 테스트는 애플리케이션의 작은 단위, 즉 클래스나 메서드가 올바르게 동작하는지 확인하는 테스트입니다. Spring에서는 @WebMvcTest@MockBean을 사용하여 컨트롤러나 서비스 로직을 손쉽게 단위 테스트할 수 있습니다.

@WebMvcTest(AuthController.class)
class AuthControllerTest {
    @MockBean
    private AuthService authService;

    @Test
    void loginTest() throws Exception {
        when(authService.login(any())).thenReturn(new JwtToken());

        mockMvc.perform(post("/auth/login")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk());
    }
}

팁:
단위 테스트는 다른 컴포넌트와 독립적으로 테스트해야 합니다. 이를 위해 @MockBean을 사용하여 필요한 의존성을 모의(mock)할 수 있습니다. 테스트에서 실제 데이터베이스나 외부 API를 호출하는 것은 지양해야 합니다.

중요한 팁:
테스트 메서드의 가독성을 높이기 위해 given-when-then 패턴을 사용해보세요. 이는 테스트 조건, 실행, 결과 검증을 명확하게 구분하는 방식입니다.

2. 통합 테스트 (Spring Integration Testing)

통합 테스트란?
통합 테스트는 여러 레이어(Controller, Service, Repository 등)가 상호작용할 때 올바르게 동작하는지 확인하는 방법입니다. @SpringBootTestMockMvc를 사용하여 애플리케이션 전체를 실행하고, 실제 시나리오에 가까운 테스트를 할 수 있습니다.

@SpringBootTest
@AutoConfigureMockMvc
class AuthIntegrationTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    void testSignup() throws Exception {
        mockMvc.perform(post("/auth/signup")
            .contentType(MediaType.APPLICATION_JSON)
            .content("{\"username\":\"test\",\"password\":\"1234\"}"))
            .andExpect(status().isOk());
    }
}

유의할 점:
통합 테스트는 애플리케이션의 실제 환경과 매우 유사하게 실행되므로 시간이 더 오래 걸릴 수 있습니다. 따라서 필요에 따라 꼭 필요한 통합 테스트만 작성하는 것이 좋습니다.

팁:
테스트에서 환경 변수를 관리하는 방법도 중요합니다. 통합 테스트는 종종 여러 환경에서 실행되므로, @TestPropertySource나 별도의 테스트 설정 파일을 사용해 환경별로 테스트를 수행할 수 있습니다.

3. 컨트롤러 테스트 (Testing Controllers)

컨트롤러 테스트란?
컨트롤러는 HTTP 요청을 처리하는 역할을 하므로, 이를 테스트할 때는 MockMvc를 사용하여 요청을 모의하고, 그에 대한 응답이 예상대로 나오는지 확인합니다.

@WebMvcTest(AuthController.class)
class AuthControllerTest {
    @Test
    void testLogin() throws Exception {
        mockMvc.perform(post("/auth/login")
            .contentType(MediaType.APPLICATION_JSON)
            .content("{\"username\":\"test\",\"password\":\"1234\"}"))
            .andExpect(status().isOk());
    }
}

팁:
테스트 코드에서 MockMvc는 매우 강력한 도구입니다. 이를 활용하여 다양한 요청을 모의할 수 있고, 상태 코드, 응답 내용 등을 쉽게 검증할 수 있습니다.

4. API 예외처리란 무엇일까?

API 예외처리란?

API는 외부 클라이언트와의 통신에서 오류가 발생할 수 있습니다. 이때 적절한 예외처리를 통해 사용자에게 명확한 에러 메시지를 전달하는 것이 중요합니다. Spring에서는 @ExceptionHandler@ControllerAdvice로 예외를 처리할 수 있습니다.

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }
}

유의할 점:
예외 처리 시, 지나치게 포괄적인 예외를 잡지 않도록 주의해야 합니다. 이는 디버깅을 어렵게 만들 수 있습니다.

팁:
예외 메시지는 사용자에게 친절하고 명확하게 전달해야 합니다. 기술적인 메시지보다는 사용자 친화적인 메시지를 제공해야 한다.

5. Spring Global 예외처리

글로벌 예외처리란?

글로벌 예외처리는 모든 컨트롤러에서 발생하는 예외를 하나의 클래스에서 처리할 수 있게 해줍니다. @ControllerAdvice를 통해 중복된 예외 처리 로직을 제거하고, 일관된 방식으로 예외를 처리할 수 있습니다.

팁:
글로벌 예외처리는 여러 컨트롤러에서 발생하는 공통된 예외를 처리할 때 유용합니다. 다양한 예외에 대해 미리 예외 처리 방식을 정의해두면 유지 보수가 쉬워집니다.

6. Error 메시지 관리하기

에러 메시지 관리하기

다양한 언어로 사용자에게 에러 메시지를 제공하기 위해 MessageSource를 활용하여 다국어를 지원할 수 있습니다. 이를 통해 에러 메시지를 일관되게 관리하고, 애플리케이션의 유연성을 높일 수 있습니다.

@Autowired
private MessageSource messageSource;

public String getErrorMessage(String code, Locale locale) {
    return messageSource.getMessage(code, null, locale);
}

팁:
에러 메시지를 관리할 때는 코드로만 제공하지 말고, 명확한 템플릿을 사용하여 에러 메시지를 쉽게 관리할 수 있도록 설정 파일로 분리하는 것이 좋습니다.

마무리

테스트의 중요성

스프링에서 단위 테스트와 통합 테스트, 그리고 AOP와 예외 처리까지 다양한 방법들을 살펴보았습니다. 테스트는 애플리케이션의 안정성을 보장하는 가장 중요한 요소 중 하나입니다. 잘 작성된 테스트는 코드의 버그를 미리 잡아내고, 빠른 피드백을 통해 개발 속도를 높이는 데 큰 기여를 합니다.

느낀 점

테스트는 단순히 작성해야 하는 것이 아니라, 더 좋은 코드를 작성하기 위한 도구입니다. 하지만 테스트를 통해 더 깨끗하고 유지 보수하기 쉬운 코드를 작성할 수 있음으로 자신의 코드를 발전시킬 수 있습니다.

profile
안녕하세요

0개의 댓글