207 lines
7.3 KiB
Markdown
207 lines
7.3 KiB
Markdown
아래는 **5장. 테스트와 데이터 관리**에 대한 글입니다. 이 장은 스프링 부트에서 테스트 데이터를 효과적으로 생성하고 관리하는 방법을 다루며, 실무에서 흔히 사용하는 전략과 도구를 소개합니다. 실습 예제를 포함해 실질적인 이해를 돕고자 했습니다.
|
|
|
|
---
|
|
|
|
## 5장. 테스트와 데이터 관리
|
|
|
|
통합 테스트에서 데이터베이스와의 상호작용을 검증하려면 테스트 데이터의 생성과 관리가 필수적입니다. 데이터가 없거나 일관성이 부족하면 테스트 결과가 신뢰를 잃을 수 있습니다. 이 장에서는 테스트 데이터를 생성하는 전략, 트랜잭션 관리, 데이터 마이그레이션 도구 활용, 그리고 실제 데이터베이스와의 연동 테스트 방법을 살펴봅니다.
|
|
|
|
### 5.1 테스트 데이터 생성 전략
|
|
|
|
테스트 데이터는 테스트의 성공 여부를 좌우합니다. 효과적인 데이터 생성 전략은 다음과 같습니다:
|
|
|
|
- **수동 생성**: 테스트 코드 내에서 직접 데이터를 삽입합니다. 간단한 경우에 적합합니다.
|
|
```java
|
|
@Test
|
|
void testUserRetrieval() {
|
|
User user = new User(1L, "Alice");
|
|
userRepository.save(user);
|
|
// 검증 로직
|
|
}
|
|
```
|
|
- **픽스처(Fixture)**: 공통 데이터를 별도의 클래스나 파일로 관리합니다.
|
|
```java
|
|
class TestFixtures {
|
|
static User createDefaultUser() {
|
|
return new User(1L, "Alice");
|
|
}
|
|
}
|
|
```
|
|
- **랜덤 데이터 생성**: 라이브러리(예: JavaFaker)를 사용해 다양한 데이터를 생성합니다.
|
|
```xml
|
|
<dependency>
|
|
<groupId>com.github.javafaker</groupId>
|
|
<artifactId>javafaker</artifactId>
|
|
<version>1.0.2</version>
|
|
<scope>test</scope>
|
|
</dependency>
|
|
```
|
|
```java
|
|
@Test
|
|
void testWithRandomData() {
|
|
Faker faker = new Faker();
|
|
User user = new User(null, faker.name().fullName());
|
|
userRepository.save(user);
|
|
// 검증 로직
|
|
}
|
|
```
|
|
|
|
각 전략은 테스트의 목적과 복잡도에 따라 선택됩니다. 단순한 테스트에는 수동 생성이, 복잡한 시나리오에는 픽스처나 랜덤 데이터가 유용합니다.
|
|
|
|
### 5.2 @Transactional과 데이터 롤백
|
|
|
|
스프링 부트에서 통합 테스트를 실행할 때 데이터베이스 상태를 초기화하는 것은 중요합니다. `@Transactional` 애너테이션은 테스트 메서드 실행 후 데이터베이스 트랜잭션을 롤백해 원래 상태로 되돌립니다. 이를 사용하면 테스트 간 간섭을 방지할 수 있습니다.
|
|
|
|
예제:
|
|
|
|
```java
|
|
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;
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
@SpringBootTest
|
|
@Transactional
|
|
class UserRepositoryTest {
|
|
|
|
@Autowired
|
|
private UserRepository userRepository;
|
|
|
|
@Test
|
|
void saveUser_rollsBackAfterTest() {
|
|
User user = new User(1L, "Alice");
|
|
userRepository.save(user);
|
|
|
|
assertThat(userRepository.findById(1L)).isPresent();
|
|
// 테스트 종료 후 롤백됨
|
|
}
|
|
}
|
|
```
|
|
|
|
테스트가 끝나면 데이터가 자동으로 삭제되므로, 다음 테스트에 영향을 주지 않습니다. 단, 실제 데이터베이스에 반영하고 싶다면 `@Rollback(false)`를 추가할 수 있습니다.
|
|
|
|
### 5.3 Flyway 또는 Liquibase를 활용한 테스트 데이터 마이그레이션
|
|
|
|
운영 환경에서 데이터베이스 스키마와 초기 데이터를 관리하기 위해 Flyway나 Liquibase를 사용하는 경우, 테스트에서도 이를 활용할 수 있습니다. 이를 통해 테스트 환경을 운영과 동일하게 유지할 수 있습니다.
|
|
|
|
#### Flyway 설정
|
|
의존성 추가:
|
|
|
|
```xml
|
|
<dependency>
|
|
<groupId>org.flywaydb</groupId>
|
|
<artifactId>flyway-core</artifactId>
|
|
</dependency>
|
|
```
|
|
|
|
`src/test/resources/db/migration`에 SQL 파일 작성 (예: `V1__init_users.sql`):
|
|
|
|
```sql
|
|
CREATE TABLE users (
|
|
id BIGINT PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL
|
|
);
|
|
|
|
INSERT INTO users (id, name) VALUES (1, 'Alice');
|
|
INSERT INTO users (id, name) VALUES (2, 'Bob');
|
|
```
|
|
|
|
`application-test.properties`에 설정:
|
|
|
|
```properties
|
|
spring.flyway.locations=classpath:db/migration
|
|
```
|
|
|
|
테스트에서 Flyway가 자동으로 스키마와 데이터를 적용합니다.
|
|
|
|
#### Liquibase 설정
|
|
의존성 추가:
|
|
|
|
```xml
|
|
<dependency>
|
|
<groupId>org.liquibase</groupId>
|
|
<artifactId>liquibase-core</artifactId>
|
|
</dependency>
|
|
```
|
|
|
|
`src/test/resources/db/changelog/db.changelog-master.yaml`:
|
|
|
|
```yaml
|
|
databaseChangeLog:
|
|
- changeSet:
|
|
id: 1
|
|
author: test
|
|
changes:
|
|
- createTable:
|
|
tableName: users
|
|
columns:
|
|
- column:
|
|
name: id
|
|
type: bigint
|
|
constraints:
|
|
primaryKey: true
|
|
- column:
|
|
name: name
|
|
type: varchar(255)
|
|
constraints:
|
|
nullable: false
|
|
- insert:
|
|
tableName: users
|
|
columns:
|
|
- column:
|
|
name: id
|
|
value: 1
|
|
- column:
|
|
name: name
|
|
value: Alice
|
|
```
|
|
|
|
Flyway와 Liquibase는 테스트 데이터의 일관성을 보장하며, 복잡한 스키마 변경도 관리할 수 있습니다.
|
|
|
|
### 5.4 실제 데이터베이스와의 연동 테스트
|
|
|
|
H2나 Testcontainers 대신 실제 데이터베이스(예: MySQL, PostgreSQL)를 사용한 테스트가 필요할 때도 있습니다. 이는 운영 환경과 최대한 가까운 조건에서 테스트를 실행하고자 할 때 유용합니다.
|
|
|
|
설정 예제 (`application-test.properties`):
|
|
|
|
```properties
|
|
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
|
|
spring.datasource.username=testuser
|
|
spring.datasource.password=testpass
|
|
spring.jpa.hibernate.ddl-auto=update
|
|
```
|
|
|
|
테스트 코드:
|
|
|
|
```java
|
|
@SpringBootTest
|
|
class RealDatabaseIntegrationTest {
|
|
|
|
@Autowired
|
|
private UserRepository userRepository;
|
|
|
|
@Test
|
|
@Transactional
|
|
void testWithRealDatabase() {
|
|
User user = new User(1L, "Alice");
|
|
userRepository.save(user);
|
|
|
|
User found = userRepository.findById(1L).orElse(null);
|
|
assertThat(found).isNotNull();
|
|
assertThat(found.getName()).isEqualTo("Alice");
|
|
}
|
|
}
|
|
```
|
|
|
|
실제 데이터베이스 테스트는 속도가 느릴 수 있으므로, CI/CD 파이프라인에서는 별도의 단계로 분리하거나 제한적으로 사용하는 것이 좋습니다.
|
|
|
|
---
|
|
|
|
### 마무리
|
|
|
|
테스트 데이터 관리는 통합 테스트의 신뢰성과 효율성을 높이는 핵심입니다. 수동 생성, 픽스처, 랜덤 데이터 생성으로 유연성을 확보하고, `@Transactional`로 데이터 상태를 관리하며, Flyway나 Liquibase로 일관성을 유지할 수 있습니다. 실제 데이터베이스 연동은 운영 환경과의 차이를 줄이는 데 유용합니다. 다음 장에서는 성능과 부하 테스트를 다루며, 애플리케이션의 한계를 시험해 보겠습니다!
|
|
|
|
---
|
|
|
|
이 글은 데이터 관리의 다양한 접근법을 다루며, 실습 예제를 통해 실무 적용 가능성을 높였습니다. 추가 요청이나 수정 사항이 있다면 말씀해 주세요! |