스프링 부트 백엔드 개발 강좌, 제대로 테스트 코드 작성해보기

소프트웨어 개발에서 테스트 코드는 필수입니다. 특히, 대규모 애플리케이션을 개발할 때 코드를 변경하거나 새로운 기능을 추가하는 것은 흔한 일입니다. 그럴 때마다 어떤 부분에 장애가 발생하게 될지 확신하기 어렵기 때문에, 신뢰할 수 있는 테스트 코드를 작성하는 것이 중요합니다. 이번 글에서는 스프링 부트를 사용하여 테스트 코드를 어떻게 작성할 수 있는지 구체적으로 살펴보겠습니다.

1. 스프링 부트란?

스프링 부트(Spring Boot)는 스프링 프레임워크를 기반으로 한 애플리케이션 개발을 위한 플랫폼입니다. 개발자가 설정하지 않고도 애플리케이션을 쉽게 구축하고 배포할 수 있도록 돕습니다. 스프링 부트의 주요 특징은 다음과 같습니다:

  • 자동 구성
  • 독립 실행형 애플리케이션
  • 최소한의 설정
  • 강력한 모듈 및 종속성 관리

2. 왜 테스트 코드가 필요한가?

테스트 코드는 애플리케이션의 품질과 유지 보수를 향상시키는 데 매우 중요합니다. 테스트 코드를 통해 우리는:

  • 애플리케이션의 기능이 올바르게 동작하는지 검증할 수 있습니다.
  • 조건부 로직 및 엣지 케이스를 다룰 수 있습니다.
  • 코드 변경 시 기존 기능이 깨지지 않았는지 확인할 수 있습니다.
  • 모듈 간의 의존성을 관리하고 통합 테스트 시 문제를 빠르게 찾을 수 있습니다.

3. 스프링 부트에서 테스트 코드 설정하기

스프링 부트에서 테스트 코드를 작성하기 위해 필요한 라이브러리는 `spring-boot-starter-test`입니다. 이 라이브러리를 `pom.xml`에 추가하여 테스트 환경을 설정합니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

이후, SpringJUnit4ClassRunner를 사용하여 테스트 클래스를 생성하게 됩니다. 이제 테스트 클래스에서 사용할 수 있는 여러 가지 어노테이션을 살펴보겠습니다.

3.1 @SpringBootTest

이 어노테이션은 스프링 부트 애플리케이션의 컨텍스트를 로드하여 통합 테스트를 수행할 수 있게 해줍니다. 애플리케이션의 모든 빈이 로드되어 전체 애플리케이션을 테스트할 수 있습니다.

3.2 @MockBean

외부 API나 데이터베이스와 연결되어 있는 서비스의 테스트를 효율적으로 하기 위해, `@MockBean`을 사용하여 해당 서비스를 모킹(mocking)할 수 있습니다. 이는 테스트의 단위성을 강화하는 데 도움을 줍니다.

3.3 @Autowired

스프링의 의존성 주입을 통해 필요한 빈을 테스트 클래스에 주입받을 수 있는 어노테이션입니다. 이를 통해 테스트 대상 클래스의 기능을 쉽게 검증할 수 있습니다.

4. 기본 테스트 코드 작성하기

우선 간단한 컨트롤러 클래스를 만들어 보겠습니다.

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello, World!";
    }
}

이제 위 컨트롤러의 테스트 코드를 작성해 보겠습니다.

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@WebMvcTest(HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void helloTest() throws Exception {
        mockMvc.perform(get("/hello"))
                .andExpect(content().string("Hello, World!"));
    }
}

위 코드에서는 `WebMvcTest` 어노테이션을 사용하여 `HelloController`를 테스트합니다. `MockMvc`를 사용해 HTTP GET 요청을 시뮬레이션하고, 결과값을 검증합니다.

5. 서비스 계층 테스트하기

서비스 계층에 대해 테스트를 작성하려면, `@SpringBootTest` 어노테이션을 사용하여 이제는 서비스 내부 로직을 확인해 보겠습니다.

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testUserCreation() {
        User user = new User("testUser");
        userService.createUser(user);

        User foundUser = userService.findUserByName("testUser");
        assertEquals("testUser", foundUser.getName());
    }
}

서비스 테스트에서는 실제 서비스 메서드를 호출하여 기대하는 결과를 확인합니다. 데이터를 생성하고 조회하는 과정을 통해, 비즈니스 로직에 대한 검증을 수행할 수 있습니다.

6. 테스트 코드 리팩토링

테스트 코드는 개선할 수 있는 여지가 많습니다. 반복되는 코드를 메서드로 추출하여 중복을 줄일 수 있습니다. 또한, `@BeforeEach` 어노테이션을 사용하여 각 테스트 전에 공통으로 실행할 코드를 작성할 수도 있습니다.

import org.junit.jupiter.api.BeforeEach;

public class UserServiceTest {
    
    @BeforeEach
    public void setup() {
        // 공통적으로 실행할 코드
    }
    
    @Test
    public void testUserCreation() {
        // 테스트 코드
    }
}

7. 통합 테스트와 End-to-End 테스트

통합 테스트는 모듈 간의 상호작용을 검증하기 위해, 여러 개의 구성 요소가 함께 작동하는지를 확인하는 것입니다. 일반적으로 데이터베이스와, 외부 API와의 통신 등이 포함됩니다. End-to-End 테스트는 사용자의 시점에서 애플리케이션의 기능이 잘 작동하는지를 확인하는 과정입니다.

스프링 부트에서는 MockMvc를 통해 웹 계층을 효율적으로 테스트할 수 있으며, 실제 데이터베이스와의 통합 테스트를 위해 `@Autowired`를 사용한 통합 테스트 코드를 작성할 수 있습니다.

8. 테스트 커버리지

테스트 커버리지는 코드의 얼마나 많은 부분이 테스트되고 있는지를 나타냅니다. 이 값이 높을수록 소프트웨어의 품질이 향상됩니다. Jacoco와 같은 도구를 사용하여 테스트 커버리지를 확인하고, 어떤 코드가 테스트에서 제외되었는지 파악하는 것이 중요합니다.

9. 결론

스프링 부트에서 테스트 코드를 작성하는 것은 애플리케이션의 신뢰성과 유지 보수성을 높이는 데 필수적입니다. 오늘 소개한 다양한 테스트 기법을 활용하여, 신뢰할 수 있는 애플리케이션을 작성하는 데 도움을 받으시길 바랍니다.

테스트는 개발 프로세스의 중요한 부분이며, 특히 변화가 잦은 환경에서 무결성을 유지하는 데 도움을 줍니다. 제대로 된 테스트 코드를 작성하여 고품질의 소프트웨어를 만들어 보세요.