[데이터베이스 2편] 데이터 접근 기술 - 테스트

wony·2024년 4월 15일

Spring

목록 보기
15/33

0. 개요

주제 : 이전에 배운 내용을 기반으로 데이터 접근 기술들을 학습한다.

1. 테스트 - 데이터베이스 분리

로컬에서 사용하는 애플리케이션 서버와 테스트에서 같은 데이터베이스를 사용하면 테스트에서 문제가 발생한다. 이런 문제를 해결하려면 테스트를 다른 환경과 철저하게 분리해야 한다.
가장 간단한 방법은 테스트 전용 데이터베이스를 별도로 운영하는 것이다.

  • H2 데이터베이스를 용도에 따라 2가지로 구분하면 된다.
    • jdbc:h2:tcp://localhost/~/test local에서 접근하는 서버 전용 데이터베이스
    • jdbc:h2:tcp://localhost/~/testcase test 케이스에서 사용하는 전용 데이터베이스

데이터베이스 파일 생성 방법

  • 데이터베이스 서버를 종료하고 다시 실행한다.
  • 사용자명은 sa 입력
  • JDBC URL에 다음 입력,
  • jdbc:h2:~/testcase (최초 한번)
  • ~/testcase.mv.db 파일 생성 확인
  • 이후부터는 jdbc:h2:tcp://localhost/~/testcase 이렇게 접속

테이블 생성
testcase 데이터베이스에도 item 테이블 생성

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)
);

접속 정보 변경
이제 접속 정보를 변경하자 참고로 main에 있는 application.properties는 그대로 유지하고, test에 있는 application.properties만 변경해야 한다.
그런데 같은 findItems() 테스트를 다시 실행하면 테스트에 실패하는 것을 볼 수 있다.

  • 테스트를 2번째 실행할 때 실패하는 이유는 testcase 데이터베이스에 접속해서 item 테이블의 데이터를 확인하면 알 수 있다.
  • 처음 테스트를 실행할 때 저장한 데이터가 계속 남아있기 때문에 두번째 테스트에 영향을 준 것이다.
  • 이 문제는 save() 같은 다른 테스트가 먼저 실행되고 나서 findItems() 를 실행할 때도 나타난다. 다른 테스트에서 이미 데이터를 추가했기 때문이다. 결과적으로 테스트 데이터가 오염된 것이다.
  • 이 문제를 해결하려면 각각의 테스트가 끝날 때 마다 해당 테스트에서 추가한 데이터를 삭제해야 한다. 그래야 다른 테스트에 영향을 주지 않는다.
  • 하지만, 스프링에서는 이러한 문제를 간단하게 해결할 수 있다.

2. 테스트 - 데이터 롤백

트랜잭션과 롤백 전략
이때 도움이 되는 것이 바로 트랜잭션이다.
테스트가 끝나고 나서 트랜잭션을 강제로 롤백해버리면 데이터가 깔끔하게 제거된다.

테스트 - @Transactional
스프링은 테스트 데이터 초기화를 위해 트랜잭션을 적용하고 롤백하는 방식을 @Transactional 애노테이션 하나로 깔끔하게 해결해준다.

@Transactional
@SpringBootTest
class ItemRepositoryTest {
	@Autowired
	ItemRepository itemRepository;
    @AfterEach
    void afterEach() {
		//MemoryItemRepository 의 경우 제한적으로 사용
		if (itemRepository instanceof MemoryItemRepository) {
			((MemoryItemRepository) itemRepository).clearStore();
		}
		//트랜잭션 롤백
		//transactionManager.rollback(status);
	}
	//...
}

@Transactional 이 테스트에 있으면 스프링은 테스트를 트랜잭션 안에서 실행하고, 테스트가 끝나면 트랜잭션을 자동으로 롤백시켜 버린다!
따라서, 계속 실행해도 이상없이 테스트를 통과한다.

강제로 커밋하기 - @Commit
@Transactional을 테스트에서 사용하면 테스트가 끝나면 바로 롤백되기 때문에 테스트 과정에서 저장한 모든 데이터가 사라진다. 당연히 이렇게 되어야 하지만, 정말 가끔은 데이터베이스에 데이터가 잘 보관되었는지 최종 결과를 눈으로 확인하고 싶을 때도 있다. 이럴 때는 다음과 같이 @Commit 을 클래스 또는 메서드에 붙이면 테스트 종료후 롤백 대신 커밋이 호출된다. 참고로 @Rollback(value = false) 를 사용해도 된다.

테스트 - 임베디드 모드 DB
H2 데이터베이스는 자바로 개발되어 있고, JVM안에서 메모리 모드로 동작하는 특별한 기능을 제공한다. 그래서 애플리케이션을 실행할 때 H2 데이터베이스도 해당 JVM 메모리에 포함해서 함께 실행할 수 있다. DB를 애플리케이션에 내장해서 함께 실행한다고 해서 임베디드 모드(Embedded mode)라 한다. 물론 애플리케이션이 종료되면 임베디드 모드로 동작하는 H2 데이터베이스도 함께 종료되고, 데이터도 모두 사라진다. 쉽게 이야기해서 애플리케이션에서 자바 메모리를 함께 사용하는 라이브러리처럼 동작하는 것이다.

스프링 부트는 개발자에게 정말 많은 편리함을 제공하는데, 임베디드 데이터베이스에 대한 설정도 기본으로 제공한다. 스프링 부트는 데이터베이스에 대한 별다른 설정이 없으면 임베디드 데이터베이스를 사용한다.

테스트에서 데이터베이스에 접근하는 설정 정보도 주석처리하자.
test - application.properties
src/test/resources/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

spring.datasource.url , spring.datasource.username를 사용하지 않도록 #을 사용해서 주석처리 했다. 이렇게 하면 데이터베이스에 접근하는 모든 설정 정보가 사라지게 된다.

이렇게 별다른 정보가 없으면 스프링 부트는 임베디드 모드로 접근하는 데이터소스(DataSource)를 만들어서 제공한다. 바로 앞서 우리가 직접 만든 데이터소스와 비슷하다 생각하면 된다. 이후에 ItemRepositoryTest 를 실행해보면 테스트가 정상 수행되는 것을 확인할 수 있다.

임베디드 데이터베이스 이름을 스프링 부트가 기본으로 제공하는 jdbc:h2:mem:testdb 로 고정하고 싶으면 application.properties 에 다음 설정을 추가하면 된다.

spring.datasource.generate-unique-name=false
profile
안녕하세요. wony입니다.

0개의 댓글