Spring DB접근 테스트 코드

바그다드·2023년 3월 26일
0

DB연동

메인 리소스에 DB 연동 설정을 한 것처럼 테스트 코드에도 DB연결 설정을 해줘야 한다.

spring.profiles.active=test
spring.datasource.url=jdbc:h2:tcp://localhost/~/**test**
spring.datasource.username=sa
  • 아래의 코드는 테스트 코드인데 @SpringBootTest를 붙이면 @SpringBootApplication이 붙은 빈을 찾아서 설정으로 사용한다.
@SpringBootTest
class ItemRepositoryTest {
  • 하지만 기존에 있던 데이터 베이스를 사용하면 문제가 발생할 수도 있는데, 예를 들어, 3명의 회원 정보를 입력하고, 이 3명의 회원 정보만 조회하려고 할 때, 쿼리문의 조건에 따라서 기존 db에 있던 다른 회원 정보까지 끌고 오는 문제가 발생할 수 있다.
  • 이를 해결할 수 있는 방법 중 하나는 데이터베이스를 분리하는 것이다.

DB분리

  • 아래의 코드를 보면 위에서 설정한 테스트코드의 DB연결 정보와 다르다는 것을 알 수 있다.
spring.profiles.active=test
spring.datasource.url=jdbc:h2:tcp://localhost/~/**testcase**
spring.datasource.username=sa

테스트 격리

  1. 테스트 코드는 다른 테스트 코드 실행에 영향을 미쳐서는 안된다
  2. 테스트 코드는 반복해서 실행할 수 있어야 한다.
  • 하지만 회원가입 메서드를 반복해서 테스트 할 때, 처음 회원가입이 성공적으로 진행되었다면, 그 다음 중복된 회원id를 입력하려 할 때 문제가 발생할 것이다.
  • 이러한 문제를 해결하는 방법 중 하나는 트랜잭션을 이용하여 롤백을 수행하는 것이다.

트랜잭션을 이용한 롤백

	@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

트랜잭션 매니저의 생성과 커밋, 롤백 등의 반복되는 코드를 대체해주는 어노테이션이다.

@Transactional
@SpringBootTest
class ItemRepositoryTest {

    @Autowired
    ItemRepository itemRepository;

    @AfterEach
    void afterEach() {
        //MemoryItemRepository 의 경우 제한적으로 사용
        if (itemRepository instanceof MemoryItemRepository) {
            ((MemoryItemRepository) itemRepository).clearStore();
        }
    }
  • 테스트에서 사용하는 @Transactional은 기본적으로 롤백으로 끝난다
    - @Commit, @Rollback(false)를 이용하면 강제로 커밋이 가능하긴 하다.
  • 어노테이션을 사용하지 않는 코드와 비교해보면 훨씬 깔끔해진 것을 확인할 수 있다!!!
  • 작동 방식은 위와 같은데 위의 모든 과정을 하나의 트랜잭션으로 관리할 수 있기 때문에 commit을 따로 하지 않아도 insert와 select를 이용한 테스트를 진행할 수 있다.

임베디드 모드 DB

  • 그런데 고작 테스트 하자고 DB를 별도로 사용하는 것은 번거로울 수 있다.
  • 검증이 끝나면 DB자체를 제거해도 된다.
  • 이럴 때 임베디드 모드를 지원하는 DB를 사용중이라면 DB분리 없이 어플리케이션에서 메모리를 사용하는 라이브러리처럼 동작하는 DB를 사용할 수 있다!!!
  • Application클래스에 아래의 코드를 추가해주자
//임베디드 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

  • 스프링 설정 파일(application.properties)에 아래 같은 코드를 추가해주면, 실행 환경에 따라 빈 등을 제어하여 사용할 수 있다.
  • 아래처럼 test/resources/application.properties파일에 프로필을 지정해주면, 해당 프로필의 이름을 가진 빈을 따로 관리할 수 있다.
spring.profiles.active=test

test 스키마 설정

  • 그런데 이렇게 설정을 해두고 테스트를 시도해보면 실패하게 될 것인다. 그 이유는 DB자체가 JVM 위에서 작동하기 때문인데 JVM이 시작하고 끝날때 DB는 초기화되고 제거된다.
    따라서 테스트를 할 때마다 테이블을 생성하고 기초 데이터를 입력하는 등의 준비가 필요하다.

Scema.sql 생성

  • 테스트가 시작할 때 스키마를 생성할 수 있도록 'test/resources/scema.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

  • 스프링 부트를 이용하면 더 쉽게 임베디드 DB를 사용할 수 있다!!!
spring.profiles.active=test
#spring.datasource.url=jdbc:h2:tcp://localhost/~/testcase
#spring.datasource.username=sa

-위의 코드처럼 db설정 데이터를 주석 처리하면 스프링 부트에서 임베디드 db를 생성할 수 있는 db를 찾아서 자동으로 초기화해준다.

이것으로 DB를 연동하여 테스트를 하는 방법을 확인해보았다.
실제 DB와 연결해서 확인하는법, 임베디드 모드로 확인하는법, 테스트 격리 등

profile
꾸준히 하자!

0개의 댓글