데이터 접근 기술 - 테스트

Single Ko·2023년 6월 7일
0

Spring 강의 정리

목록 보기
27/31

데이터베이스 연동했을때 테스트를 어떻게 하면 좋을까?

테스트는 항상 동작이 일정해야 한다. 데이터베이스에 저장,삭제,업데이트를 하면 데이터베이스의 자료에 실제로 데이터의 차이가 생긴다. 그렇게 된다면 항상 일정한 결과를 내지 못할 수 있다.(실제 DB의 데이터가 변경되었기 때문에)

테스트에서 중요한것은 바로 격리성이다. 외부의 변수 없이 테스트 환경을 분리해야된다. 이런 환경을 만들기위해 몇가지 방법을 알아보자

데이터베이스 분리

  1. 별도의 테스트 데이터베이스를 운영한다.

    테스트 패키지의 application.properties에 datasource를 설정하고 profile을 분리해두면 된다.

  2. 테스트 데이터베이스를 운영해도 데이터는 축적된다.

    처음에 만들때는 테스트가 실행이 된다. 하지만 다시 테스트를 돌리니 실패했다. 이와 같은 이유가 발생하는 것은 저장한 데이터가 계속 남아있기 때문에 두번째 테스트에 영향을 준 것이다. 이러면 안된다. 각 테스트는 격리되어서 영향을 주면 안된다. 이 문제를 해결 하려면 테스트가 끝날때마다 데이터를 다시 초기화해주거나, 지워야된다.

테스트의 매우 중요한 원칙

  • 테스트는 다른 테스트와 격리해야 한다.
  • 테스트는 반복해서 실행할 수 있어야 한다.

테스트가 끝날 때 마다 추가한 데이터에 DELETE SQL을 사용해도 되겠지만, 궁극적인 해결책은 아니다. 만약 테스트 과정에서 데이트럴 이미 추가했는데, 테스트가 실행되는 도중에 예외가 발생하거나 애플리케이션이 종료되어 버리면 테스트 종료 시점에 DELETE SQL을 호출하지 못할 수도 있다. 그렇다면 결국 데이터가 남아있게 된다.

데이터 롤백

테스트가 끝나고 나서 트랜잭션을 강제로 롤백해버리면 데이터가 깔끔하게 제거된다. 테스트를 하면서 데이터를 이미 저장했는데, 중간에 테스트가 실패해서 롤백을 호출하지 못해도 괜찮다.

트랜잭션을 커밋하지 않았기 때문에 데이터베이스에 해당 데이터가 반영되지 않는다. 이렇게 트랜잭션을 활용하면 테스트가 끝나고 나서 데이터를 깔끔하게 원래 상태로 되돌릴 수 있다.

//트랜잭션 관련 코드
@Autowired
PlatformTransactionManager transactionManager;\

TransactionStatus status;

@BeforeEach
void beforeEach() {
	//트랜잭션 시작
	status = transactionManager.getTransaction(new DefaultTransactionDefinition());
}

@AfterEach
void afterEach() {
	//트랜잭션 롤백
	transactionManager.rollback(status);
}

이렇게 직접 트랜잭션 코드를 @BeforeEach@afterEach를 이용해서 직접 넣어줘도 된다. 하지만 매번 테스트를 짤 때 마다 이렇게 적는 것은 꽤 귀찮은 작업이다.

@Transactional 을 기억할 것이다. 스프링이 AOP를 이용해 제공하는 트랜잭션기능이다. 다만 이 @Transactional 애노테이션이 테스트에서 사용되면 스프링은 테스트를 트랜잭션안에서 실행하고, 테스트가 끝나면 자동으로 롤백시켜 버린다.

메서드나 테스트에 @Transactional만 붙여버리면 된다.

테스트가 끝난 후 개발자가 직접 데이터를 삭제하지 않아도 되는 편리함을 제공한다.

테스트 실행 중에 데이터를 등록하고 중간에 테스트가 강제로 종료되어도 걱정이 없다. 이 경우 트랜잭션을 커밋하지 않기 때문에, 데이터는 자동으로 롤백된다. (보통 데이터베이스 커넥션이 끊어지면 자동으로 롤백되어 버린다.)

트랜잭션 범위 안에서 테스트를 진행하기 때문에 동시에 다른 테스트가 진행되어도 서로 영향을 주지 않는 장점이 있다.

참고
만약 테스트 DB에 커밋을 하고 싶다면 @Commit이나 @Rollback(false)를 사용하면 된다.

임베디드 모드 DB

단순히 테스트를 검증할 용도로만 사용하기 때문에 테스트가 끝나면 데이터베이스의 데이터를
모두 삭제해도 된다. 더 나아가서 테스트가 끝나면 데이터베이스 자체를 제거해도 된다.

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

스프링 부트 - 기본 SQL 스크립트를 사용해서 데이터베이스를 초기화하는 기능

메모리 DB는 애플리케이션이 종료될 때 함께 사라지기 때문에, 애플리케이션 실행 시점에 데이터베이스 테이블도 새로 만들어주어야 한다.

JDBC나 JdbcTemplate를 직접 사용해서 테이블을 생성하는 DDL을 호출해도 되지만, 너무 불편하다. 스프링 부트는 SQL 스크립트를 실행해서 애플리케이션 로딩 시점에 데이터베이스를 초기화하는 기능을 제공한다.
다음 파일을 생성하자.

위치가 src/test 이다. 이 부분을 주의하자. 그리고 파일 이름도 맞아야 한다.

src/test/resources/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)
);

그렇다면 이 임베디드 모드는 어떻게 사용하는 것인가?

이 임베디드 모드를 따로 빈으로 빼서 프로필을 통해 설정해도 된다.

@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;
}

하지만 스프링 부트는 특별한 설정이 없어도 임베디드 모드로 동작이 된다.
jdbc:h2:mem이 붙어있는것을 볼 수 있다. 이것이 바로 h2 임베디드 모드가 작동된다는 것이다.

참조 : 김영한님의 스프링 강의를 보고 공부를 위해 정리한 글입니다.

profile
공부 정리 블로그

0개의 댓글