ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링 DB 데이터접근 기술 - 테스트
    Spring-Boot/스프링 DB 2편 - 데이터 접근 기술 2022. 9. 14. 18:41

    * @SpringBootTest *

    - @SpringBootTest는 @SpringBootApplication 를 찾아서 설정으로 사용한다.

     

    테스트의 원칙

    - * 테스트는 다른 테스트와 격리해야 한다. *

    - * 테스트는 반복해서 실행할 수 있어야 한다. *

     

     

    * 트랜잭션과 롤백 전략 *

    - 테스트가 끝나고 나서 트랜잭션을 강제로 롤백해버리면 데이터가 깔끔하게 제거된다.

     

     @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(new DefaultTransactionDefinition()) 로 트랜잭션을 시작한다.

    - @AfterEach : 각각의 테스트 케이스가 완료된 직후에 호출

        ㆍ따라서 여기서 트랜잭션을 롤백하면 된다.

        ㆍ`transactionManager.rollback(status)`로 트랜잭션을 롤백

     

    @Transactional 원리

    - 스프링이 제공하는 @Transactional 애노테이션은 로직이 성공적으로 수행되면 커밋되도록 동작

    - 그런데 @Transactional 은 테스트에서 사용하면 아주 특별하게 동작

        ㆍ테스트를 트랜잭션안에서 실행하고, 테스트가 끝나면 트랜잭션을 자동으로 롤백

     

    * 정리 *

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

    - 테스트 실행 중에 데이터를 등록하고 중간에 테스트가 강제로 종료되어도 걱정 없다.

      이 경우 트랜잭션을 커밋하지 않기 때문에, 데이터는 자동으로 롤백된다.

       (보통 데이터베이스 커넥션이 끊어지면 자동으로 롤백되어 버린다.)

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

    - @Transactional 덕분에 아주 편리하게 다음 원칙을 지킬 수 있다.

        ㆍ테스트는 다른 테스트와 격리해야 한다

        ㆍ테스트는 반보해서 실행할 수 있어야 한다

     

    * 강제로 커밋하기 - @Commit *

    - @Transactional 을 테스트에서 사용하면 테스트가 끝나면 바로 롤백되기 때문에 테스트 과정에서 저장한 모든 데이터가 사라진다.

    - 데이터베이스에 데이터가 잘 보관되었는지 확인하고 싶을 때 @Commit을 클래스 또는 메서드에 붙이면 테스트 종료 후 롤백 대신 커밋이 호출 된다.

     


    * 임베디드 모드 *

    - H2 데이터베이스는 자바로 개발되어 있고, JVM안에서 메모리 모드로 동작하는 특별한 기능을 제공한다.

    - 그래서 애플리케이션을 실행할 때 H2데이터베이스도 해당 JVM메모리에 포함해서 함께 실행할 수 있다.

    - DB를 애플리케이션에 내장해서 함께 실행한다고 해서 임베디드 모드(Embeded mode)라 한다.

    - 물론 애플리케이션이 종료되면 임베디드 모드로 동작하는 H2 데이터베이스도 함께 종료되고, 데이터도 모두 사라짐

    - 쉽게 이야기해서 애플리케이션에서 자바 메모리를 함께 사용하는 라이브러리처럼 동작하는 것이다.

     

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

    - dataSource()

        ㆍ`jdbc:h2:mem:db` : 이 부분이 중요하다. 데이터소스를 만들때 이렇게 적으면 임베디드 모드(메모리 모드)로

                                             동작하는 H2 데이터베이스를 사용할 수 있다

        ㆍ`DB_CLOSE_DELAY=-1` : 임베디드모드에서는 데이터베이스 커넥션 연결이 모두 끊어지면 데이터베이스도

                                                     종료되는데, 그것을 방지하는 설정이다

        ㆍ이 데이터소스를 사용하면 메모리 DB를 사용할 수 있다. 

     

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

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

    - JDBC나 JdbcTemplate를 직접 사용해서 테이블을 생성하는 DDL을 호출해도 되지만, 너무 불편하다

    - 스프링 부트는 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)
    );

    스프링 부트와 임베디드 모드

    - test properties의 DB 정보 삭제

    - 이렇게 별다른 정보가 없으면 스프링 부트는 임베디드 모드로 접근하는 데이터소스(`DataSource`)를 만들어서 제공한다. 

     

    - 참고로 로그를 보면 `jdbc:h2:mem` 뒤에 임의의 데이터베이스 이름이 들어가 있다.

    - 이것은 혹시라도 여러 데이터소스가 사용될 때 같은 데이터베이스를 사용하면서 발생하는 충돌을 방지하기 위한 스프링 부트가 임의의 이름을 부여한 것이다.

    댓글

Designed by Tistory.