[스프링 DB 2편] #3. 데이터 접근 기술 - 테스트 #550
Develop-KIM
started this conversation in
동환
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
테스트 - 데이터베이스 연동
main - application.properties
src/main/resources/application.propertiestest - application.properties
src/test/resources/application.propertiesspring.profiles.active=test테스트 케이스는
src/test에 있기 때문에 실행하면src/test에 있는application.properties파일이 우선순위를 가지고 실행된다.test - application.properties 수정
src/test/resources/application.properties테스트 실행 - 로컬DB
@SpringBootTestItemRepositoryTest는@SpringBootTest를 사용한다.@SpringBootTest는@SpringBootApplication를 찾아서 설정으로 사용한다.@SpringBootApplication@SpringBootApplication설정이 과거에는MemoryConfig.class를 사용하다가이제는
JdbcTemplateV3Config.class를 사용하도록 변경되었다. 따라서 테스트도JdbcTemplate을 통해 실제 데이터베이스를 호출하게 된다.MemoryItemRepositoryJdbcTemplateItemRepositoryV3실행 결과
updateitem(): 성공save(): 성공findItems(): 실패findItems()는 다음과 같은 오류를 내면서 실패했다.findItems()코드를 확인해보면 상품을 3개 저장하고, 조회한다.ItemRepositoryTest.findItems()
결과적으로 테스트에서 저정한 3개의 데이터가 조회 되어야 하는데, 기대보다 더 많은 데이터가 조회되었다.
실패 원인
TestDataInit은 프로필이local일때만 동작하는데 테스트 케이스를 실행할 때는프로필이
spring.profiles.active=test이기 때문에 초기화 데이터가 추가되지는 않는다.테스트 - 데이터베이스 분리
jdbc:h2:tcp://localhost/~/testlocal에서 접근하는 서버 전용 데이터베이스jdbc:h2:tcp://localhost/~/testcasetest 케이스에서 사용하는 전용 데이터베이스테이블 생성
접속 정보 변경
이제 접속 정보를 변경하자 참고로
main에 있는application.properties는 그대로 유지하고test에 있는application.properties만 변경해야 한다.main - application.properties
src/main/resources/application.propertiestest - application.properties
src/test/resources/application.properties테스트 실행
findItems()테스트만 단독으로 실행해보자findItems()테스트를 다시 실행하면 테스트에 실패한다.testcase데이터베이스에 접속해서item테이블의 데이터를 확인하면 알 수 있다.save()같은 다른 테스트가 먼저 실행되고 나서findItems()를 실행할 때도 나타난다.다른 테스 트에서 이미 데이터를 추가했기 때문이다. 결과적으로 테스트 데이터가 오염된 것이다.
테스트에서 매우 중요한 원칙은 다음과 같다.
테스트 - 데이터 롤백
트랜잭션과 롤백 전략
테스트가 끝나고 나서 트랜잭션을 강제로 롤백해버리면 데이터가 깔끔하게 제거된다.
테스트를 하면서 데이터를 이미 저장했는데 중간에 테스트가 실패해서 롤백을 호출하지 못해도 괜찮다.
트랜잭션을 커밋하지 않았기 때문에 데이터베이스에 해당 데이터가 반영되지 않는다.
이렇게 트랜잭션을 활용하면 테스트가 끝나고 나서 데이터를 깔끔하게 원래 상태로 되돌릴 수 있다.
예를 들어서 다음 순서와 같이 각각의 테스트 실행 직전에 트랜잭션을 시작하고 각각의 테스트 실행 직후에 트랜잭션을 롤백해야 한다.
그래야 다음 테스트에 데이터로 인한 영향을 주지 않는다.
테스트에 직접 트랜잭션 추가
PlatformTransactionManager를 주입 받아서 사용하면 된다.참고로 스프링 부트는 자동으로 적절한 트랜잭션 매니저를 스프링 빈으로 등록해준다.
@BeforeEach: 각각의 테스트 케이스를 실행하기 직전에 호출된다. 따라서 여기서 트랜잭션을 시작하면 된다.그러면 각각의 테스트를 트랜잭션 범위 안에서 실행할 수 있다.
transactionManager.getTransaction(new DefaultTransactionDefinition())로 트랜잭션을 시작한다.@AfterEach: 각각의 테스트 케이스가 완료된 직후에 호출된다. 따라서 여기서 트랜잭션을 롤백하면 된다.그러면 데이터를 트랜잭션 실행 전 상태로 복구할 수 있다.
transactionManager.rollback(status)로 트랜잭션을 롤백한다.테스트 -
@Transactional스프링은 테스트 데이터 초기화를 위해 트랜잭션을 적용하고 롤백하는 방식을
@Transactional애노테이션 하나로 깔끔하게 해결해준다.@Transactional 원리스프링이 제공하는
@Transactional애노테이션은 로직이 성공적으로 수행되면 커밋하도록 동작한다.그런데
@Transactional애노테이션을 테스트에서 사용하면 아주 특별하게 동작한다.@Transactional이 테스트에 있으면 스프링은 테스트를 트랜잭션 안에서 실행하고 테스트가 끝나면 트랜잭션을 자동으로 롤백시켜 버린다!@Transactional이 적용된 테스트 동작 방식@Transactional애노테이션이 테스트 메서드나 클래스에 있으면 먼저 트랜잭션을 시작한다.트랜잭션은 기본적으로 전파되기 때문에, 리포지토리에서 사용하는 JdbcTemplate도 같은 트랜잭션을 사용한다.
item1,item2,item3를 데이터베이스에 저장한다.물론 테스트가 리포지토리를 호출하고, 리포지토리는 JdbcTemplate을 사용해서 데이터를 저장한다.
item1,item2,item3이 조회되었다.SELECT SQL도 같은 트랜잭션을 사용하기 때문에 저장한 데이터를 조회할 수 있다. 다른 트랜잭션에서는 해당 데이터를 확인할 수 없다.
assertThat()으로 검증이 모두 끝난다.@Transactional이 테스트에 있으면 테스트가 끝날때 트랜잭션을 강제로 롤백한다.item1,item2,item3의 데이터가 제거된다.정리
데이터는 자동으로 롤백된다. (보통 데이터베이스 커넥션이 끊어지면 자동으로 롤백되어 버린다.)
@Transactional덕분에 아주 편리하게 다음 원칙을 지킬수 있게 되었다.강제로 커밋하기 -
@Commit@Transactional을 테스트에서 사용하면 테스트가 끝나면 바로 롤백되기 때문에 테스트 과정에서 저장한 모든 데이터가 사라진다.당연히 이렇게 되어야 하지만 정말 가끔은 데이터베이스에 데이터가 잘 보관되었는지 최종 결과를 눈으로 확인하고 싶을 때도 있다.
이럴 때는 다음과 같이
@Commit을 클래스 또는 메서드에 붙이면 테스트 종료후 롤백 대신 커밋이 호출된다.참고로
@Rollback(value = false)를 사용해도 된다.테스트 - 임베디드 모드 DB
임베디드 모드
H2 데이터베이스는 자바로 개발되어 있고, JVM안에서 메모리 모드로 동작하는 특별한 기능을 제공한다.
그래서 애플리케이션을 실행할 때 H2 데이터베이스도 해당 JVM 메모리에 포함해서 함께 실행할 수 있다.
DB를 애플리케이션에 내장해서 함께 실행한다고 해서 임베디드 모드(Embedded mode)라 한다.
물론 애플리케이션이 종료되면 임베디드 모드로 동작하는 H2 데이터베이스도 함께 종료되고, 데이터도 모두 사라진다.
쉽게 이야기해서 애플리케이션에서 자바메모리를 함께 사용하는 라이브러리처럼 동작하는 것이다.
임베디드 모드 직접 사용
ItemServiceApplication - 추가
Table "ITEM" not found이 부분이 핵심이다. 데이터베이스 테이블이 없는 것이다.테스트를 실행하기 전에 테이블을 먼저 생성해주어야 한다. 수동으로 할 수도 있지만 스프링 부트는 이 문제를 해결할 아주 편리한 기능을 제공해준다.
스프링 부트 - 기본 SQL 스크립트를 사용해서 데이터베이스를 초기화하는 기능
메모리 DB는 애플리케이션이 종료될 때 함께 사라지기 때문에, 애플리케이션 실행 시점에 데이터베이스 테이블도 새로 만들어주어야 한다.
JDBC나 JdbcTemplate를 직접 사용해서 테이블을 생성하는 DDL을 호출해도 되지만, 너무 불편하다.
스프링 부트는 SQL 스크립트를 실행해서 애플리케이션 로딩 시점에 데이터베이스를 초기화하는 기능을 제공한다.
src/test/resources/schema.sql실행
ItemRepositoryTest를 실행해보면 드디어 테스트가 정상 수행되는 것을 확인할 수 있다.로그 확인
src/test/resources/application.properteisSQL 스트립트 로그
테스트 - 스프링 부트와 임베디드 모드
스프링 부트는 개발자에게 정말 많은 편리함을 제공하는데, 임베디드 데이터베이스에 대한 설정도 기본으로 제공한다.
스프링 부트는 데이터베이스에 대한 별다른 설정이 없으면 임베디드 데이터베이스를 사용한다.
test - application.properties
src/test/resources/application.properties앞서 설정한 데이터베이스에 접근하는 모든 설정 정보를 제거해주었다.
이렇게 별다른 정보가 없으면 스프링 부트는 임베디드 모드로 접근하는 데이터소스(
DataSource)를 만들어서 제공한다.실행
앞전과 똑같이 정상적으로 동작하는 것을 확인 할 수 있다.
참고로 로그를 보면 다음 부분을 확인할 수 있는데
jdbc:h2:mem뒤에 임의의 데이터베이스 이름이 들어가 있다.이것은 혹시라도 여러 데이터소스가 사용될 때 같은 데이터베이스를 사용하면서 발생하는 충돌을 방지하기 위해 스프링 부트가 임의의 이름을 부여한 것이다.
conn0: url=jdbc:h2:mem:d8fb3a29-caf7-4b37-9b6c-b0eed9985454임베디드 데이터베이스 이름을 스프링 부트가 기본으로 제공하는
jdbc:h2:mem:testdb로 고정하고 싶으면application.properties에 다음 설정을 추가하면 된다.spring.datasource.generate-unique-name=falseBeta Was this translation helpful? Give feedback.
All reactions