테스트에 @Transactional 추가해도 안되네요..

미해결질문
Taehee-kim-dev 프로필

기선님 안녕하세요!

저번에 JPA 테스트에서, 같은 객체에 대해서 assertThat을 두 번 했었습니다.

저는 JPA가 select문을 한 번만 날리고 그 객체로 assertThat을 두 번 할거라 예상했지만,

예상과 다르게 JPA는 select문을 각 asserThat문 마다 한 번씩, 총 두 번 날렸습니다.

그래서 제가 질문을 올렸었습니다. 왜 select문이 한 번만 날아가지 않고 두 번 다 날아가는지 모르겠다구요..

기선님이 @Transactional 을 테스트에 붙여보라고 하셨습니다.

사실 그때도 service 클래스에 @Transactional이 붙어있긴 했습니다.

기선님이 말씀하신 대로 테스트에 @Transactional을 붙여서 했는데 해결이 되지않아 다시 질문드립니다!!

일단 서비스 클래스입니다. 원래 @Transactional 어노테이션은 붙어있었습니다.

기선님이 말씀하신대로 테스트에 @Transactional 추가했습니다.

먼저 save 후 확인하는 테스트 코드입니다.

테스트 결과입니다. 테스트는 성공했습니다. 하지만 전과 같이 select문이 두 번 날아갔습니다.

그리고 왜인지는 모르겠는데 전에는 롤백이 없었는데,

test함수에 @Transactional을 추가하니 롤백이 생겼습니다ㅠ

save 후 update하여 확인하는 테스트 코드입니다.

에러가 났습니다..

insert후 select했는데 왜 났는지 모르겠네요 ㅠ

그 밑에 select문을 두 번 날리는 것은 같더군요..

왜 select를 한번만 하지 않는지, save 테스트에서 롤백은 왜 하는지, update테스트는 왜 에러가 나는지 모르겠습니다..ㅠ

백기선 프로필
백기선 3달 전

일단 코드를 깃헙에 올려서 공유해 주시면 좋겠네요.

백기선 프로필
백기선 3달 전

제 환경에서 비슷하게 작성해서 테스트를 해봤으나. 재현이 되지 않습니다.

@Transactional
@SpringBootTest
class BookRepositoryTest {

@Autowired BookRepository bookRepository;

@Test
void test() {
Book book = new Book();
book.setTitle("spring");
bookRepository.save(book);

List<Book> all = bookRepository.findAll();

assertEquals("spring", all.get(0).getTitle());
assertEquals("spring", all.get(0).getTitle());
}

} 

Select 쿼리가 하나만 발생합니다.

@Transactional 애노테이션을 추가했을 때 롤백이 발생하는건 스프링 테스트가 제공하는 기능입니다. 테스트에 사용한 트랜잭션은 기본을 롤백으로 처리해서 다른 테스트에 영향을 주지 않기 위한 기능입니다. 자세한 내용은 스프링 레퍼런스를 참고하세요.

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/testing.html#testing-introduction

그리고 수정하는 테스트가 깨진 이유는 코드를 보지 않고서는 저도 잘 모르겠습니다.

Taehee-kim-dev 프로필
Taehee-kim-dev 3달 전

https://github.com/taehee-kim-dev/mysecretconcerns-webservice

해당 코드 전체 프로젝트입니다!!

직접 테스트도 해주시고 자세한 설명 정말 감사합니다 ㅠ

Taehee-kim-dev 프로필
Taehee-kim-dev 3달 전

제가 random_port설정으로 했는데 이게 관련있나요??

백기선 프로필
백기선 3달 전

코드를 봤는데 select는 findAll 할때 한번 발생했고 그 다음에 오는 select는 deleteAll() 때문에 발생한거네요.

@After
public void cleanUp(){
postRepository.deleteAll();
}

deleteAll() 구현체가 기존 데이터를 가져와서 전부 delete(E entity) 를 호출해주기 때문입니다.

Taehee-kim-dev 프로필
Taehee-kim-dev 3달 전

아... 정말 감사합니다!! 그걸 생각 못했네요 ㅠ

근데 궁금한게 더 있는데요,

Q1. Service 클래스에 @Transactional 붙였으면 테스트에선 안붙여도 테스트 할 때 트랜잭션 처리 하는거죠?

Q2. 위에 마지막 깨진 테스트요.. 성공했을 때와 실패했을 때의 차이점은 테스트에 @Transactional을 추가로 붙인건데 왜 깨졌을까요??

자세한 답변 정말 감사드립니다!!

백기선 프로필
백기선 3달 전

더 이상은 좀 더 학습을 하신 다음에 한달 뒤 쯤 그래도 모르시겠으면 알려드리겠습니다.

지식공유자 되기
많은 사람들에게 배움의 기회를 주고,
경제적 보상을 받아보세요.
지식공유참여
기업 교육을 위한 인프런
“인프런 비즈니스” 를 통해 모든 팀원이 인프런의 강의들을
자유롭게 학습하는 환경을 제공하세요.
인프런 비즈니스