해결된 질문
작성
·
306
0
안녕하세요 강사님.
테스트를 따라하다가 궁금한게 생겨서 질문드립니다!
저는 JUnit5를 통해서 테스트를 작성했고. 우선 코드를 첨부하겠습니다!
package jpa.boot.jpaboot.service;
import jpa.boot.jpaboot.domain.Member;
import jpa.boot.jpaboot.repository.MemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.*;
@SpringBootTest
@Transactional
class MemberServiceTest {
@Autowired
MemberService memberService;
@Autowired
MemberRepository memberRepository;
@Test
void 회원가입() {
// given
Member member = new Member();
member.setName("Kim");
// when
Long saveId = memberService.join(member);
// then
assertThat(memberRepository.findOne(saveId)).isEqualTo(member);
}
}
여기서 테스트는 정상적으로 통과되는데,
클래스 레벨에 @Transactional을 지우니까 테스트가 실패했습니다.
실패 메세지는 다음과 같습니다
Expecting:
<jpa.boot.jpaboot.domain.Member@9bf63d2>
to be equal to:
<jpa.boot.jpaboot.domain.Member@22ff1372>
but was not.
isSameAs로 실행해도 마찬가지더라고요.
1차 캐시에서 가져와서 비교하기 때문에 테스트가 통과한다는 건 알겠지만, Transactional을 지우면 어떤 원리로 테스트가 실패하는지 모르겠습니다.
답변 1
9
안녕하세요. Han jinho님^^ 좋은 질문입니다.
스프링 컨테이너와 JPA를 함께 사용하면, 영속성 컨테스트의 생존 범위를 트랜잭션에 맞춥니다.
따라서 @Transactional이 있으면 테스트 시작 직전에 DB 트랜잭션을 만들어서 테스트를 실행하고, 테스트가 종료되는 시점에 트랜잭션도 종료됩니다. 이렇게 되면 트랜잭션이 회원가입 테스트 동안 유지되기 때문에 영속성 컨테스트도 같은 영속성 컨테스트가 유지됩니다.
그런데 @Transactional이 없다면 어떻게 될까요?
아마 MemberService에 트랜잭션이 있을 거에요. 따라서 MemberService.join()을 시작할 때 트랜잭션이 만들어지고 MemberService.join()이 반환되는 시점에 트랜잭션이 종료됩니다. 따라서 영속성 컨텍스트도 이때 종료되어 버립니다.
이후 테스트에서 memberRepository.findOne(saveId)를 호출하면, 트랜잭션이 없는 상태에서 memberRepository.findOne()이 호출되는데요. 이렇게 되면 영속성 컨테스트가 조회시점에 바로 생겼다가 조회 후에 바로 없어진다고 생각하시면 됩니다.
결론적으로 둘은 완전히 다른 영속성 컨테스트에서 객체를 조회했기 때문에 다른 객체가 조회됩니다.
감사합니다.