테스트 - 데이터베이스 분리
테스트 코드
@Test
void findItems() {
//given
Item item1 = new Item("itemA-1", 10000, 10);
Item item2 = new Item("itemA-2", 20000, 20);
Item item3 = new Item("itemB-1", 30000, 30);
itemRepository.save(item1);
itemRepository.save(item2);
itemRepository.save(item3);
//여기서 3개 이상이 조회되는 문제가 발생
test(null, null, item1, item2, item3);
}
로컬에서 사용하는 애플리케이션 서버와 테스트에서 같은 DB를 사용해 테스트에서 문제가 발생
-> 테스트를 다른 환경과 분리해야 함
간단한 방법은 DB를 용도에 따라 구분하는 것
-> 첫번째 테스트는 성공 but 두번째부터 실패
-> 첫번째 테스트에 저장한 데이터가 계속 남아있기 때문
=> 테스트는 다른 테스트와 격리해야 함
=> 테스트는 반복해 실행할 수 있어야 함
테스트 - 데이터 롤백
트랜잭션과 롤백 전략
@SpringBootTest
class ItemRepositoryTest {
@Autowired
ItemRepository itemRepository;
//트랜잭션 관련 코드
@Autowired
PlatformTransactionManager transactionManager;
TransactionStatus status;
@BeforeEach
void beforeEach() {
//트랜잭션 시작
status = transactionManager.getTransaction(new
DefaultTransactionDefinition());
}
@AfterEach
void afterEach() {
//MemoryItemRepository 의 경우 제한적으로 사용
if(itemRepository instanceof MemoryItemRepository) {
((MemoryItemRepository) itemRepository).clearStore();
}
//트랜잭션 롤백
transactionManager.rollback(status);
}
//...
}
-> 트랜잭션 관리자는 PlatformTransactionManager를 주입 받아 사용 : 스프링 부트가 적절한 트랜잭션 매니저를 스프링 빈으로 등록해줌
-> @BeforeEach : 테스트 실행 직전 호출되므로 여기서 트랜잭션 시작 : transactionManager.getTransaction으로 트랜잭션 시작
-> @AfterEach : 테스트 완료 직후 호출되므로 여기서 트랜잭션 롤백 : transactionManager.rollback()으로 트랜잭션 롤백
테스트 - @Transactional
스프링은 @Transactional 애노테이션 하나로 트랜잭션을 적용 및 롤백해준다.
@Transactional
@SpringBootTest
class ItemRepositoryTest {
@Autowired
ItemRepository itemRepository;
@AfterEach
void afterEach() {
//MemoryItemRepository 의 경우 제한적으로 사용
if(itemRepository instanceof MemoryItemRepository) {
((MemoryItemRepository) itemRepository).clearStore();
}
}
//...
}
@Transactional 원리

1. @Transactional 애노테이션이 테스트 메서드나 클래스에 있으면 먼저 트랜잭션을 시작한다.
2. 테스트 로직을 실행한다. 테스트가 끝날 때 까지 모든 로직은 트랜잭션 안에서 수행된다.
-> 트랜잭션은 기본적으로 전파되기 때문에, 리포지토리에서 사용하는 JdbcTemplate도 같은 트랜잭션을 사용한다.
3. 테스트 실행 중 item1, item2, item3을 DB에 저장한다.
-> 테스트가 리포지토리를 호출하고 리포지토리는 JdbcTemplate을 사용해 데이터를 저장한다.
4. 검증을 위해 item1, item2, item3을 조회한다.
5. @Transactional이 테스트에 있으면 테스트가 끝날때 트랜잭션을 강제로 롤백한다.
7. 롤백에 의해 DB의 item1, item2, item3 데이터가 제거된다.
* 테스트 케이스의 메서드나 클래스에 @Transactional을 직접 붙여 사용할 때만 이렇게 동작한다.
트랜잭션을 테스트에서 시작하기 때문에 서비스나 리포지토리에 있는 @Transactional도 테스트에 시작한 트랜잭션에 참여한다.
= 같은 트랜잭션을 사용한다.
=> 테스트가 끝난 후 데이터를 직접 삭제하지 않아도 됨
=> 테스트 실행 중 데이터를 등록하고 중간에 테스트가 강제로 종료되어도 걱정이 없음(보통 DB 커넥션이 끊어지면 자동으로 롤백됨)
=> 트랜잭션 범위 안에서 테스트를 진행하기 때문에 다른 테스트가 진행되어도 서로 영향을 주지 않음
@Commit
가끔 DB에 데이터가 잘 보관되었는지 확인을 위해 커밋을 해야하는 경우도 존재
이럴 경우, @Commit 또는 @Rollback(value = false)를 사용하면 된다.
테스트 - 임베디드 모드 DB
테스트를 위해 별도 DB를 설치, 운영하는 것은 상당히 번잡한 작업
임베디드 모드
H2 데이터베이스는 자바로 개발되어 있고, JVM 안에서 메모리 모드로 동작하는 특별한 기능을 제공함
애플리케이션 실행 시 H2 DB도 해당 JVM 메모리에 포함해 함께 실행할 수 있음
DB를 애플리케이션에 내장해 함께 실행한다고 하여 임베디드 모드라 함
-> 애플리케이션이 종료되면 임베디드 모드의 H2 DB도 종료되고 데이터도 모두 사라짐
-> 애플리케이션에서 자바 메모리를 함께 사용하는 라이브러리처럼 동작
@Bean
@Profile("test")
public DataSource dataSource() {
log.info("메모리 데이터베이스 초기화");
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
-> Profile("test") : 프로필이 test인 경우에만 데이터소스를 스프링 빈으로 등록
-> dataSource()
: jdbc:h2:mem:db 처럼 적을 시 임베디드 모드로 동작하는 H2 DB를 사용할 수 있음(중요)
: DB_CLOSE_DELAY=-1 은 임베디드 모드에서 DB 커넥션이 끊어지면 데이터베이스가 종료되는데 이것을 방지하는 설정
스프링 부트 : 기본 SQL 스크립트를 사용해 DB를 초기화하는 기능 제공
* 메모리 DB는 애플리케이션이 종료될 때 함께 사라지기 때문에, 실행 시점에 테이블도 새로 만들어주어야 함!
* 스프링 부트는 SQL 스크립트를 실행해 애플리케이션 로딩 시점에 DB를 초기화하는 기능을 제공함
schema.sql 생성
drop table if exists item CASCADE;
create table item
(
id bigint generated by default as identity,
item_name varchar(10),
price integer,
quantity integer,
primary key (id)
);
테스트 - 스프링 부트와 임베디드 모드
사실 스프링 부트는 임베디드 DB에 대한 설정도 기본으로 제공한다.
데이터베이스에 접근하는 설정 정보 주석처리
-> 별다른 정보가 없으면 스프링 부트는 임베디드 모드로 접근하는 데이터소스를 만들어 제공함
application.properties
spring.profiles.active=test
#spring.datasource.url=jdbc:h2:tcp://localhost/~/testcase
#spring.datasource.username=sa
#jdbcTemplate sql log
logging.level.org.springframework.jdbc=debug
임베디드 DB 이름을 스프링 부트가 기본으로 제공하는 jdbc:h2:mem:testdb로 고정하고 싶으면 application.properties에 다음 설정을 추가하면 됨!
spring.datasource.generate-unique-name=false
'인프런 > 스프링 DB 2편 - 데이터 접근 활용 기술' 카테고리의 다른 글
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 6. 데이터 접근 기술 - 스프링 데이터 JPA (0) | 2024.08.11 |
|---|---|
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 5. JPA (0) | 2024.08.04 |
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 4. MyBatis (1) | 2024.07.28 |
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 2. 데이터 접근 기술 - 스프링 JdbcTemplate (1) | 2024.07.18 |
| [인프런] 스프링 DB 2편 - 데이터 접근 기술 활용 / 1. 데이터 접근 기술 - 시작 (0) | 2024.07.03 |