테스트코드("Transaction silently rolled back because it has been marked as rollback-only") 에러

백승호·2022년 6월 25일
1

인프런 강의를 듣던 중 테스트 코드를 짜면서 특이한 에러를 마주했습니다.

"롤백을 해야만 한다고 지정되어서 트랜잭션이 롤백이 됐다" ??
우선 테스트 코드를 어떻게 짰는지 부터 알아보겠습니다.

테스트 코드

@RunWith(SpringRunner.class)
@SpringBootTest 
@Transactional
public class MemberServiceTest {

    @Autowired
    MemberService memberService;
    @Autowired
    MemberRepository memberRepository;

    @Test
    @Rollback(value = false)
    public void join() throws Exception{
    
        //given
        Member member = new Member();
        member.setName("Seungho");
        
        //when
        Long savedId = memberService.join(member);

        //then

        assertEquals(member, memberRepository.findOne(savedId));
        assertEquals(member, memberService.findOne(savedId));
        assertNotNull(member);

    }

    @Test
    @Rollback(value = false)
    public void duplicatedMember() throws Exception{
    
        //given
        Member member1 = new Member();
        member1.setName("Seungho");

        Member member2 = new Member();
        member2.setName("Seungho");
        
        //when
        try{

            memberService.join(member1);
 
            memberService.join(member2);
        }catch (IllegalStateException e){
            assertEquals(e.getMessage(),"이미 존재하는 회원입니다.");
            System.out.println(e.getMessage());
            return;
        }

        //then
        fail("예외가 안 발생해서 여기까지 오면 무조건 에러를 발생시키는 메소드. 즉 예외테스트 실패시 이 문장이 뜬다!");
    }

}

우선 첫번째 join 테스트는 무난하게 성공할 수 밖에 없는 테스트 형식이고 두번째 duplicatedMember 메소드를 테스트코드 작성하기 위해 위에서 이미 Seungho라는 name으로 member 등록해놨던 아이디를 다시 중복되게 테스트 해봤습니다.

예상

  • member1과 member2가 join되기도 전에 이미 member1에서 join할 때 db에 같은 이름으이 존재하는 것을 확인하고 지정해놓은 IllegalStateException을 뿜을 것이라고 판단.
  • 예외를 catch로 잡아서 에러 메시지 비교해주고 return으로 끝내면 테스트 코드가 통과할 것이라고 판단

그러나 테스트 코드는 실패했고, 위에서 언급했던 "Transaction silently rolled back because it has been marked as rollback-only" 에러가 발생했습니다.

일단 문제는 직감했습니다.
@Rollback(value=false)라고 해놓은 부분인데요, 이 부분은 @Transaction에서 이루어지는 테스트 코드들이 default로 롤백이 되기 마련인데, 그 부분을 false로 지정해서 롤백을 안 하게 할려고 했었습니다.

그런데 try catch로 에러 처리를 해줌으로써 db에 저장은 안 돼도 에러가 나지 않고 테스트코드가 통과할 것 이라고 생각해서 관련된 에러를 찾아보던 중 이 블로그를 발견하고 문제를 이해했습니다.

Unchecked Exception


위 두가지 이미지를 통해 보면 크게 "Exception"과 "Error"가 나뉘고 에러는 Unchecked Exception으로 예외 발생시 항상 롤백을 해야한다고 나와있습니다. 반면 예외는 Checked Exception으로 롤백 하지 않는다고 나와 있습니다.

제가 짠 코드에서는

//when
        try{

            memberService.join(member1);
 
            memberService.join(member2);
        }catch (IllegalStateException e){
            assertEquals(e.getMessage(),"이미 존재하는 회원입니다.");
            System.out.println(e.getMessage());
            return;
        }

이렇게 IllegalStateException을 날리고 있는데 이는 RuntimeException을 상속한 예외입니다. RuntimeException은 unchecked exception으로 분류되어 rollback을 해야한 하는데, @Rollback(value= false)로 롤백을 강제했으니 이 부분에서 위와 같은 에러가 발생한 것입니다.

이렇게 중복검사에 걸린 부분은 오히려 db에 저장이 안 되는 로직이 맞으니 위 코드에서 Rollback 어노테이셔만 지워주면 문제는 해결이 됩니다.

그러나 이런 어노테이션 문제가 아닌 문제로 문제가 발생한다면 앞서 언급한 블로그에 해결 방안도 제시 되어 있으니 참고하시길 바랍니다!

profile
처음처럼

0개의 댓글