JUnit5에서 DataJpaTest를 사용한 테스트와 데이터베이스 상호 작용
1. DataJpaTest 어노테이션과 테스트 진행
DataJpaTest 어노테이션은 JUnit5에서 JPA 연결 테스트를 작성하는 데 사용되는 편리한 기능입니다. 이 어노테이션은 다음과 같은 특징을 제공합니다.
- 자동 트랜잭션 관리: 각 테스트 메서드를 시작하고 종료할 때 자동으로 트랜잭션을 시작하고 롤백합니다.
- 스프링 부트 테스트 설정: 스프링 부트 테스트 환경을 자동으로 구성하여 JPA 및 기타 관련 빈을 사용할 수 있도록 합니다.
- 데이터베이스 준비 및 정리: 테스트 전에 테스트 데이터베이스를 준비하고 테스트 후 정리합니다.
DataJpaTest를 사용한 테스트 진행 과정은 다음과 같습니다.
- 테스트 클래스 작성:
@DataJpaTest
어노테이션을 사용하여 테스트 클래스를 정의합니다. - 필요한 빈 주입: 테스트 클래스의 생성자 또는 테스트 메서드에서 필요한 JPA 리포지토리 및 기타 빈을 주입합니다.
- 테스트 메서드 작성: 각 테스트 케이스를 나타내는
@Test
어노테이션이 있는 메서드를 작성합니다. - 테스트 로직 구현: 테스트 메서드 내에서 테스트 로직을 구현합니다. 이 로직에는 데이터베이스 조작, 엔티티 검증, 예외 처리 등이 포함될 수 있습니다.
- 테스트 실행: JUnit5 테스트 러너를 사용하여 테스트 클래스를 실행합니다.
2. 객체 Update 테스트 및 SQL UPDATE Query 생성
문제
@DataJpaTest
를 사용하여 객체 Update 테스트를 작성했지만, 단순히 repository의 save
메서드를 사용하면 SQL UPDATE Query
가 생성되지 않습니다. 이는 @DataJpaTest
가 테스트 메서드마다 자동으로 트랜잭션을 시작하고 롤백하기 때문입니다.
해결 방법
SQL UPDATE Query
를 생성하고 실제 데이터베이스 변경을 확인하기 위해서는 다음과 같은 방법을 사용할 수 있습니다.
1. @DataJpaTest 제거 및 직접 트랜잭션 관리
@DataJpaTest
어노테이션을 제거하고 테스트 메서드 내에서 직접 트랜잭션을 시작하고 커밋하거나 롤백해야 합니다. @Transactional
어노테이션 또는 EntityManager
메서드를 사용하여 트랜잭션을 관리할 수 있습니다.
@Test
@Transactional // 트랜잭션 시작 및 커밋
void updateArticleTest() {
// Given
Article article = articleRepository.findById(1L).orElseThrow();
String updatedHashtag = "#springboot";
article.setHashtag(updatedHashtag);
// When
articleRepository.save(article); // 엔티티 변경 내용 저장
// Then
Article savedArticle = articleRepository.findById(1L).orElseThrow();
assertThat(savedArticle.getHashtag()).isEqualTo(updatedHashtag);
}
2. @Transactional(propagation = Propagation.NOT_SUPPORTED) 사용
@DataJpaTest
어노테이션을 유지하면서 @Transactional(propagation = Propagation.NOT_SUPPORTED)
를 사용할 수 있습니다. 이 어노테이션은 테스트 메서드 내부에서 발생하는 트랜잭션만 영향을 받고, 테스트 범위 외부의 트랜잭션에는 영향을 미치지 않습니다. 즉, 테스트 실행 후 데이터베이스 변경 사항이 유지됩니다.
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED) // 트랜잭션 롤백 방지
void updateArticleTest() {
// Given
Article article = articleRepository.findById(1L).orElseThrow();
String updatedHashtag = "#springboot";
article.setHashtag(updatedHashtag);
// When
articleRepository.save(article); // 엔티티 변경 내용 저장
// Then
Article savedArticle = articleRepository.findById(1L).orElseThrow();
assertThat(savedArticle.getHashtag()).isEqualTo(updatedHashtag);
}
3. @EntityManager.persist() 사용
앞선 두 가지 방법은 트랜잭션 관리 방식을 변경하는 것이었습니다. 세 번째 방법인 @EntityManager.persist()
는 트랜잭션 관리를 완전히 우회하여 객체를 직접 데이터베이스에 저장하는 방법입니다.
장점
- 트랜잭션 관리 오버헤드를 줄일 수 있습니다.
- 테스트 케이스에서 더 세밀하게 데이터 저장 시점을 제어할 수 있습니다.
단점
- JPA가 제공하는 캐싱 및 변경 감지 기능을 사용하지 않게 됩니다.
- 직접 트랜잭션을 관리하지 않으면 데이터 무결성 문제가 발생할 수 있습니다.
사용 방법
- 테스트 클래스에 @Autowired EntityManager 주입:
테스트 클래스에서@Autowired
어노테이션을 사용하여EntityManager
객체를 주입합니다. - 엔티티 변경:
테스트 로직에서 수정하고자 하는 엔티티를 가져와 필요한 속성을 변경합니다. - @EntityManager.persist() 호출:
EntityManager
객체의persist()
메서드를 사용하여 변경된 엔티티를 데이터베이스에 저장합니다.
예시 코드
@Test
void updateArticleTest(@Autowired EntityManager entityManager) {
// Given
Article article = articleRepository.findById(1L).orElseThrow();
String updatedHashtag = "#springboot";
article.setHashtag(updatedHashtag);
// When
entityManager.persist(article); // 트랜잭션 관리 없이 직접 저장
// Then (선택적)
Article savedArticle = articleRepository.findById(1L).orElseThrow();
assertThat(savedArticle.getHashtag()).isEqualTo(updatedHashtag);
}
주의 사항
@EntityManager.persist()
를 사용할 경우, 테스트 메서드 내에서 직접 데이터 변경을 관리해야 합니다. jména 다음과 같은 사항을 고려해야 합니다.
- 트랜잭션 관리: 필요한 경우,
entityManager.flush()
메서드를 사용하여 변경 내용을 데이터베이스에 반영하고, 테스트 종료 전에entityManager.clear()
메서드를 사용하여 영속 컨텍스트를 초기화하는 것이 좋습니다. - 데이터 무결성: 테스트 데이터 준비 및 정리가 필요할 수 있으며, 테스트 간의 데이터 간섭을 방지하기 위해 주의하여 설계해야 합니다.
결론
하지만 사실 원하는 것은 한 가지입니다. DB에 데이터가 남을 필요도 없습니다. 오히려 원하지 않습니다. 테스트 이후 데이터가 모두 사라지기를 원합니다.
하지만 Update SQL Query는 만들어져 console에서 식별하기 위해서는 save()
메서드 대신 saveAndFlus()
메서드를 사용해야 합니다.
'프레임워크 > 자바 스프링' 카테고리의 다른 글
[spring cloud][ecommerce] 개요 & 구성 (0) | 2024.08.12 |
---|---|
콘서트 예매 서비스에서 발생할 수 있는 동시성 이슈와 처리 (0) | 2024.07.20 |
JPA 연결 테스트 코드 (0) | 2024.05.28 |
[그냥 보는] application.yaml (0) | 2024.05.28 |
로깅 출력 어느 것으로?(feat.Interpolation vs Concatenation) (0) | 2024.04.22 |