@DataJpaTest
로 OneToOne 연관관계가 있는 객체를 저장 후 조회하는데, owner
에는 null 이 들어와서 테스트 코드는 실패한다.
왜 owner
에는 setUp()
에서 저장한 person
이 조회되지 않는걸까?
@Test
@Transactional
void getOwnerTest() {
personId = personRepository.save(Person.of("name1")).personId();
animalId = animalRepository.save(Animal.of(personId)).animalId();
var animal = animalRepository.findById(animalId).get();
assertThat(animal.owner().personId()).isEqualTo(personId);
assertThat(animal.owner().name()).isEqualTo("name1");
}
setUp(), getOwnerTest() 로 나누어져 있지만, 사실은 위와 같이 하나의 트랜잭션에서 수행되는 것과 같다.
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
void getOwnerTest() {
personId = personRepository.save(Person.of("name1")).personId();
animalId = animalRepository.save(Animal.of(personId)).animalId();
var animal = animalRepository.findById(animalId).get();
assertThat(animal.owner().personId()).isEqualTo(personId);
assertThat(animal.owner().name()).isEqualTo("name1");
}
Propagation.NOT_SUPPORTED
로 기존 Transaction 없이 수행되도록 한다.
이 경우에는 자동 롤백처리가 안 되니 조심하자.
@Transactional Propagation 속성 | 설명 |
---|---|
REQUIRED (기본값) | 기존에 생성된 Transaction이 있으면 참여하고 없다면 새로운 Transaction을 생성합니다. |
REQUIRES_NEW | 항상 새로운 Transaction을 생성합니다. |
SUPPORTS | 기존에 생성된 중인 Transaction이 있을 때만 참여하고 없다면 Transaction 없이 진행합니다. |
NOT_SUPPORTED | 기존에 생성된 Transaction 이 있든 말든 Transaction 없이 진행합니다. |
MANDATORY | 이미 진행 중인 Transaction이 있으면 참여하고, 기존에 생성된 Transaction 이 없다면 예외를 발생시킵니다. |
NESTED | 이미 진행 중인 Transaction이 있다면 중첩으로 Transaction을 생성하여 진행합니다. |
@SpringBootTest
를 사용하자@SpringBootTest
를 사용해서 다른 트랜잭션에서 수행되도록 한다.
스프링 테스트 프레임워크에서는 TransactionalTestExecutionListener.java 가 활성화 되어 Transactional을 관리한다. TransactionalTestExecutionListener
는 클래스 또는 메서드 레벨에 적용한 @Transactional
을 감지해 자동으로 메서드에 트랜잭션을 건다.
beforeTestMethod()
에서 isRollback()
를 호출한다.
isRollback()
은 기본적으로 true 를 반환한다.
afterTestMethod()
에서 txContext.endTransaction()
를 호출한다. 이때, defaultRollback
/ flaggedRollback
필드가 true 인 것을 알 수 있다.
TransactionContext.java 은 flaggedRollback
가 true 인 경우 this.transactionManager.rollback(this.transactionStatus)
을 호출하고, 위에서 봤던 로그를 찍게된다.
Rolled back transaction for test: [DefaultTestContext@ff684e1 testClass = RelationTest
@Test
@Rollback(value = false)
void getOwnerTest() {
var animal = animalRepository.findById(animalId).get();
assertThat(animal.owner().personId()).isEqualTo(personId);
assertThat(animal.owner().name()).isEqualTo("name1");
}
해당 옵션의 기본값은 true 이다. 롤백하고 싶지 않은 경우 false 로 옵션을 변경하면 롤백이 되지 않는다.
TransactionalTestExecutionListener.java 의 isRollback() 을 보면 우리가 준 rollback 어노테이션의 옵션값인 false 가 들어가는 것을 볼 수 있다.
TransactionalTestExecutionListener
가 자동으로 트랜잭션을 걸어준다.