🚀 스프링 부트 테스트: @SpringBootTest 활용하기 🧪
안녕, 친구들! 오늘은 정말 흥미진진한 주제로 찾아왔어. 바로 스프링 부트 테스트와 @SpringBootTest 어노테이션에 대해 깊이 파헤쳐볼 거야. 😎 이 글을 읽고 나면 너희도 테스트 마스터가 될 수 있을 거야! 자, 준비됐니? 그럼 시작해보자고!
잠깐! 혹시 너희 중에 재능넷(https://www.jaenung.net)이라는 사이트 들어봤어? 여기서 프로그래밍 실력을 공유하고 배울 수 있대. 나중에 우리가 배운 스프링 부트 테스트 기술로 멋진 프로젝트를 만들어서 공유해보는 건 어때? 🤔
1. 스프링 부트 테스트란 뭐야? 🤷♂️
자, 먼저 스프링 부트 테스트가 뭔지부터 알아보자. 간단히 말해서, 스프링 부트 테스트는 우리가 만든 애플리케이션이 제대로 동작하는지 확인하는 방법이야. 마치 요리사가 음식 맛을 보는 것처럼, 우리도 코드의 '맛'을 보는 거지! 😋
스프링 부트 테스트는 우리 애플리케이션의 품질을 보장하고, 버그를 미리 잡아내는 데 큰 도움을 줘.
이게 왜 중요하냐고? 상상해봐. 네가 만든 앱이 수많은 사용자들에게 배포됐는데, 갑자기 큰 오류가 발생한다면? 😱 아찔하지 않아? 그래서 우리는 테스트를 통해 이런 일이 일어나지 않도록 미리미리 준비하는 거야.
위 그림을 보면 스프링 부트 테스트의 기본 흐름을 이해할 수 있어. 코드를 작성하고, 테스트를 실행한 다음, 그 결과를 확인하는 과정이지. 이 과정을 반복하면서 우리는 점점 더 안정적이고 믿을 수 있는 애플리케이션을 만들어갈 수 있어.
2. @SpringBootTest 어노테이션의 비밀 🕵️♀️
자, 이제 우리의 주인공 @SpringBootTest 어노테이션에 대해 알아볼 차례야. 이 녀석, 겉보기엔 별 거 아닌 것 같지만, 실은 엄청난 힘을 가지고 있어!
@SpringBootTest는 스프링 부트 애플리케이션을 위한 테스트 환경을 손쉽게 구성해주는 마법 같은 어노테이션이야.
이 어노테이션을 사용하면, 실제 애플리케이션과 거의 동일한 환경에서 테스트를 실행할 수 있지. cool하지 않아? 😎
🚨 주의! @SpringBootTest는 강력하지만, 그만큼 리소스를 많이 사용해. 모든 테스트에 무작정 사용하면 테스트 실행 시간이 길어질 수 있으니 꼭 필요한 경우에만 사용하자!
자, 이제 @SpringBootTest를 어떻게 사용하는지 간단한 예제를 통해 살펴보자.
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MyAwesomeTest {
@Test
void contextLoads() {
// 여기서 테스트 로직을 작성해
}
}
위 코드를 보면, 클래스 위에 @SpringBootTest 어노테이션을 붙였어. 이렇게 하면 스프링 부트가 테스트를 위한 애플리케이션 컨텍스트를 생성해줘. 그리고 @Test 어노테이션이 붙은 메서드가 실제 테스트 케이스가 되는 거지.
하지만 이게 다가 아니야! @SpringBootTest는 여러 가지 옵션을 제공해서 테스트 환경을 더 세밀하게 제어할 수 있어. 예를 들어:
- webEnvironment: 웹 환경 설정
- properties: 테스트용 프로퍼티 설정
- classes: 특정 설정 클래스만 로드
이런 옵션들을 활용하면 정말 다양한 상황에 맞는 테스트를 작성할 수 있어. 예를 들어, 웹 서버를 실제로 띄우고 싶다면 이렇게 할 수 있지:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyWebTest {
// 테스트 코드
}
이렇게 하면 실제 웹 서버가 랜덤 포트로 시작되고, 그 상태에서 테스트가 실행돼. 마치 실제 운영 환경과 비슷한 상황에서 테스트할 수 있는 거지!
3. @SpringBootTest의 내부 동작 원리 🔍
자, 이제 @SpringBootTest가 어떻게 동작하는지 더 자세히 들여다볼 시간이야. 이 녀석이 어떻게 그렇게 강력한 테스트 환경을 만들어내는 걸까? 🤔
@SpringBootTest는 스프링 부트의 자동 구성(Auto-configuration) 기능을 최대한 활용해.
이게 무슨 말이냐면, 실제 애플리케이션을 시작할 때처럼 필요한 모든 빈(Bean)들을 자동으로 등록하고 구성한다는 거야. 정말 똑똑하지?
@SpringBootTest의 동작 과정을 간단히 정리하면 이렇게 돼:
- 테스트 클래스를 스캔하고 @SpringBootTest 어노테이션을 발견해.
- 스프링 부트 애플리케이션 컨텍스트를 생성해.
- @SpringBootApplication이 붙은 메인 클래스를 찾아 구성 정보로 사용해.
- application.properties (또는 application.yml) 파일의 설정을 로드해.
- 자동 구성을 통해 필요한 빈들을 등록하고 초기화해.
- 테스트에 필요한 추가적인 구성을 적용해 (예: 웹 환경 설정).
- 모든 준비가 끝나면 테스트 메서드를 실행해.
이 과정을 거치면서 @SpringBootTest는 우리가 실제 애플리케이션을 실행했을 때와 거의 동일한 환경을 만들어내는 거야. 그래서 통합 테스트를 수행하기에 완벽한 조건이 되는 거지!
하지만 이런 강력한 기능에는 당연히 대가가 따르지. 바로 테스트 실행 시간이 길어질 수 있다는 거야. 전체 애플리케이션 컨텍스트를 로드하는 게 꽤 무거운 작업이거든. 그래서 단위 테스트처럼 가벼운 테스트에는 @SpringBootTest 대신 다른 가벼운 테스트 어노테이션을 사용하는 게 좋아.
💡 꿀팁: 테스트 실행 시간을 줄이고 싶다면, 테스트 클래스들 사이에 @DirtiesContext 어노테이션을 적절히 사용해봐. 이 어노테이션은 스프링에게 "이 테스트가 끝나면 컨텍스트를 새로 만들어야 해"라고 알려주는 역할을 해. 컨텍스트를 재사용할 수 있는 상황에서는 테스트 실행 속도를 크게 높일 수 있지!
4. @SpringBootTest 실전 활용 예제 💪
자, 이제 @SpringBootTest를 실제로 어떻게 활용하는지 몇 가지 예제를 통해 살펴보자. 이론만 알면 뭐해, 실전에서 써먹을 줄 알아야지! 😉
4.1 기본적인 통합 테스트
먼저 가장 기본적인 형태의 통합 테스트부터 시작해볼까?
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Test
void testCreateUser() {
User user = new User("testuser", "test@example.com");
User createdUser = userService.createUser(user);
assertNotNull(createdUser.getId());
assertEquals("testuser", createdUser.getUsername());
assertEquals("test@example.com", createdUser.getEmail());
}
}
이 테스트에서는 UserService를 주입받아 실제로 사용자를 생성하는 기능을 테스트하고 있어. @SpringBootTest 덕분에 실제 애플리케이션과 동일한 환경에서 UserService가 제대로 동작하는지 확인할 수 있지.
4.2 웹 애플리케이션 테스트
웹 애플리케이션을 테스트할 때는 실제 HTTP 요청을 보내고 응답을 확인해야 할 때가 있어. 이럴 때 @SpringBootTest와 TestRestTemplate을 함께 사용하면 아주 편리해!
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void testGetUser() {
String url = "http://localhost:" + port + "/api/users/1";
ResponseEntity<user> response = restTemplate.getForEntity(url, User.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
assertEquals("testuser", response.getBody().getUsername());
}
}
</user>
여기서는 @SpringBootTest의 webEnvironment 옵션을 RANDOM_PORT로 설정해서 실제 웹 서버를 랜덤 포트로 시작했어. 그리고 TestRestTemplate을 사용해 HTTP GET 요청을 보내고 응답을 확인하고 있지. 이렇게 하면 컨트롤러부터 서비스, 리포지토리까지 전체 플로우를 한 번에 테스트할 수 있어!
4.3 데이터베이스 연동 테스트
데이터베이스와 연동된 기능을 테스트할 때도 @SpringBootTest가 큰 도움이 돼. 예를 들어, JPA를 사용하는 리포지토리를 테스트한다고 해보자.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest
@Transactional
public class UserRepositoryIntegrationTest {
@Autowired
private UserRepository userRepository;
@Test
void testSaveAndFindUser() {
User user = new User("testuser", "test@example.com");
userRepository.save(user);
User foundUser = userRepository.findByUsername("testuser");
assertNotNull(foundUser);
assertEquals("test@example.com", foundUser.getEmail());
}
}
이 테스트에서는 @Transactional 어노테이션을 추가로 사용했어. 이렇게 하면 각 테스트 메서드가 트랜잭션 내에서 실행되고, 테스트가 끝나면 자동으로 롤백돼. 덕분에 테스트 데이터가 실제 DB에 남지 않아서 테스트 격리성을 유지할 수 있지.
4.4 외부 서비스 모킹 테스트
때로는 외부 서비스에 의존하는 기능을 테스트해야 할 때가 있어. 이럴 때는 @MockBean을 사용해서 외부 서비스를 모킹할 수 있어.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
@SpringBootTest
public class WeatherServiceIntegrationTest {
@Autowired
private WeatherService weatherService;
@MockBean
private ExternalWeatherApi externalWeatherApi;
@Test
void testGetWeatherForecast() {
when(externalWeatherApi.getForecast("Seoul"))
.thenReturn(new Forecast("Sunny", 25));
Weather weather = weatherService.getWeatherForecast("Seoul");
assertEquals("Sunny", weather.getCondition());
assertEquals(25, weather.getTemperature());
}
}
여기서는 ExternalWeatherApi를 @MockBean으로 선언해서 실제 외부 API를 호출하지 않고도 WeatherService를 테스트할 수 있게 했어. 이렇게 하면 네트워크 문제나 외부 서비스의 불안정성에 영향받지 않고 안정적으로 테스트를 수행할 수 있지.
🚨 주의! @MockBean을 사용할 때는 주의가 필요해. 이 어노테이션은 스프링 컨텍스트를 수정하기 때문에, 과도하게 사용하면 테스트 실행 시간이 길어질 수 있어. 꼭 필요한 경우에만 사용하는 게 좋아!
5. @SpringBootTest의 고급 기능 🚀
자, 이제 @SpringBootTest의 더 고급스러운 기능들을 살펴볼 차례야. 이 녀석은 정말 다재다능해서, 우리가 상상하는 거의 모든 테스트 시나리오를 커버할 수 있어!
5.1 테스트용 설정 오버라이딩
때로는 테스트 환경에서만 특별한 설정을 적용하고 싶을 때가 있지? 이럴 때 @SpringBootTest의 properties 속성을 사용하면 돼.
@SpringBootTest(properties = {
"spring.datasource.url=jdbc:h2:mem:testdb",
"spring.jpa.hibernate.ddl-auto=create-drop"
})
public class CustomConfigTest {
// 테스트 코드
}