스프링 부트 2.6.5 버전을 기준으로 작성됨
H2 데이터베이스 Version 2.2.224 (2023-09-17)
강의에서 제공하는 기본 소스코드에서 DB 관련 기술만 학습
application.properties
는 src/main
과 src/test
는 다른 구역이므로 테스트에서도 동일한 환경을 사용한다면 src/test
의 application.properties
도 설정해주자.
spring.profiles.active=test
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.username=sa
logging.level.org.springframework.jdbc=debug
dataSource가 이 정보를 토대서 생성됨
@SpringBootTest
애노테이션은 @SpringBootApplication
을 찾아서 설정으로 사용한다.
H2 데이터베이스로 테스트를 실패할 경우 2가지를 확인하자
1. H2 데이터베이스 실행 여부
2. H2 데이터베이스의 데이터 초기화 여부
로컬에서 사용하는 애플리케이션 서버와 테스트에서 같은 데이터베이스를 사용해서 문제가 발생한다.
가장 간단한 방법
테스트 전용 데이터베이스를 별도로 운영
jdbc:h2:tcp://localhost/~/test local
에서 접근하는 서버 전용 데이터베이스jdbc:h2:tcp://localhost/~/testcase test
케이스에서 사용하는 전용 데이터베이스생성방법은
서버 재실행 -> url에 testcase등으로 바꾸고 연결 하면 testcase.mv.db
가 생성된다.
이제 jdbc:h2:tcp://localhost/~/testcase
이 url로 접속
src/test appliation.properties도 변경
spring.profiles.active=test
spring.datasource.url=jdbc:h2:tcp://localhost/~/testcase
spring.datasource.username=sa
테스트가 끝날때마다 delete sql을 사용하는 것은 테스트 도중 예외 발생시 애플리케이션이 종료되버리면 delete sql을 실행할 수 없기 때문에 좋지 않다.
트랜잭션을 사용하면 해결된다.
1. 트랜잭션 시작
2. 테스트 A 실행
3. 트랜잭션 롤백
4. 트랜잭션 시작
5. 테스트 B 실행
6. 트랜잭션 롤백
.
.
.
테스트는 각각의 테스트 실행 전 후로 동작하는 @BeforeEach
, @AfterEach
라는 편리한 기능을 제공
//트랜잭션 관련 코드
@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);
}
사실 더 좋은 방법이 있는데 @Transactional
애노테이션 하나만 붙이면 된다. 이러면 각 테스트 전 후로 트랜잭션 시작, 롤백을 자동으로 해준다. 해당 기능은 test에서만 적용된다.
메서드나 클래스에 @Transactional
이 있으면 트랜잭션을 시작한다.
테스트 메서드가 끝나면 트랜잭션을 강제로 롤백한다.
참고
트랜잭션을 테스트에서 시작하기 때문에 서비스, 리포지토리에 있는 @Transactional 도 테스트에서
시작한 트랜잭션에 참여한다. 라고 하는데 트랜잭션 전파라는 것을 배워야 하나보다. 같은 트랜잭션 범위에 들어간다 라고 알아두자
@Commit
@Commit
애노테이션을 클래스 또는 메서드에 붙이면 테스트 종료 후 롤백 대신 커밋이 호출된다.
@Rollback(value = false)
도 마찬가지이다.
테스트 케이스 실행을 위해 별도의 데이터베이스 설치, 설정, 운영은 번잡한 작업이다.
H2 데이터베이스는 자바로 개발되었고, JVM 안에서 메모리 모드로 동작하는 특별한 기능을 제공한다.
즉, JVM 메모리에 H2 데이터베이스를 포함해서 함께 실행이 가능하다. 이를 임베디드 모드
라 한다.
(DB를 애플리케이션에 내장)
애플리케이션이 종료되면 임베디드 모드로 동작하는 H2 데이터베이스도 함께 종료, 데이터도 모두 삭제
// 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")
dataSource()
jdbc:h2:mem:db
: 중요! 데이터소스를 만들때 이렇게만 적으면 임베디드 모드(메모리 모DB_CLOSE_DELAY=-1
: 임베디드 모드에서는 데이터베이스 커넥션 연결이 모두 끊어지면 데이터베이스도 종료되는 것을 방지하는 설정스프링 부트는 임베디드 데이터베이스에 대한 설정도 기본으로 제공한다.
데이터베이스에 대한 별다른 설정이 없으면 임베디드 데이터베이스를 사용
데이터베이스에 접근하는 DataSource 설정 정보도 주석처리
application.properties의 datasource 설정 정보도 주석처리
이로써 스프링부트가 임베디드 데이터베이스를 제공한다.
conn0: url=jdbc:h2:mem:0e4053f9-xxxx-xxxx-xxxx-xxxxxxxxxxx user=SA]
테스트를 실행하면 이런 식으로 임베디드 모드가 연결된 것을 확인 할 수 있다.
메모리 DB는 애플리케이션 종료될 때 함께 사라지기에 실행 시점에 데이터베이스 테이블도 새로 만들어 줘야한다.
스프링 부트는 SQL 스크립트를 실행해서 애플리케이션 로딩 시점에 데이터베이스를 초기화하는 기능 제공
src/test/resource/schema.sql
해당 위치에 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)
);
sql문을 입력해놓으면 된다.
🔖 학습내용 출처