[Java Spring] @BeforeAll은 transaction이 적용되지 않는다.

김태훈·2023년 9월 28일
post-thumbnail

통합 테스트코드를 작성중에, @BeforeAll로 유저 정보를 먼저 등록하거나, 게시물 몇가지를 미리 등록하고자 하였다.

이 때, 클래스 레벨에 @Transactional을 붙여서 테스트가 끝나면 해당 정보들이 삭제되기를 기대했다.\
하지만 테스트 코드를 반복적으로 돌릴 때마다, @Transaction이 끝나면 Rollback이 되지 않아서 데이터가 계속 쌓이는 문제가 생겼다.

문제가 된 코드는 다음과 같다.

@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Transactional
class QuestionIntegrationTest {
    @Autowired
    QuestionRepository questionRepository;
    @Autowired
    MyRepository myRepository;
    @Autowired
    QuestionService questionService;
    @Autowired
    MemberRepository memberRepository;

    static Long saveMemberId;
    @BeforeAll
    void init() {
        System.out.println("몇번 반복?");
        saveUser();
        saveQuestions();
    }

    private void saveUser() {
        MemberSaveDto memberSaveDto = MemberSaveDto.builder()
                .userName("taehoon")
                .loginType(LoginType.NON_SOCIAL)
                .userPw("a1234567!")
                .userEmail("test@gmail.com")
                .build();
        MemberProfileEntity memberProfileEntity = MemberProfileEntity.builder()
                .nickname("taehoon")
                .profileImageFilePath("taehoon-image")
                .introduce("introduce of taehoon")
                .userTags(List.of("tag1", "tag2"))
                .build();

        SaveMemberResponseDto saveMemberResponseDto = memberRepository.save(NonSocialMember.createNonSocialMember(memberSaveDto));
        saveMemberId = saveMemberResponseDto.getId();
        myRepository.createProfile(saveMemberId, memberProfileEntity);
    }

    private void saveQuestions() {
        QuestionSaveDto firstQuestionSaveDto = QuestionSaveDto.builder().title("첫번째 질문")
                .text("질문 내용")
                .tags(List.of("tag1", "tag2"))
                .build();

        QuestionSaveDto secondQuestionSaveDto = QuestionSaveDto.builder().title("두번째 질문")
                .text("질문 내용")
                .tags(List.of("tag1", "tag2"))
                .build();

        QuestionSaveDto thirdQuestionSaveDto = QuestionSaveDto.builder().title("세번째 질문")
                .text("질문 내용")
                .tags(List.of("tag1", "tag2"))
                .build();
        questionService.createQuestion(firstQuestionSaveDto,saveMemberId);
        questionService.createQuestion(secondQuestionSaveDto,saveMemberId);
        questionService.createQuestion(thirdQuestionSaveDto,saveMemberId);
    }

    @Test
    @DisplayName("질문 생성")
    @Transactional
    void createQuestion(){
        QuestionSaveDto questionSaveDto = QuestionSaveDto.builder().title("테스트 제목")
                .text("질문 내용")
                .tags(List.of("tag1", "tag2"))
                .build();
        assertThatCode(()->questionService.createQuestion(questionSaveDto,saveMemberId)).doesNotThrowAnyException();
    }
    @Nested
    @DisplayName("파라미터에 알맞는 정렬된 메모 리스트 가져오기")
    class getOrderedQuestions{
        @Test
        @DisplayName("최신순으로 정렬")
        void getLatest(){
            List<Question> latestQuestionList = questionService.getQuestion(OrderType.NEW);
            assertThat(latestQuestionList).hasSize(3);

        }
    }
}

테스트를 수행하면 할 수록, createMemo로 만들어진 정보가 계속해서 쌓여갔다.

공식문서의 설명은 다음과 같았다.

https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/tx.html

Annotating a test method with @Transactional causes the test to be run within a transaction that is, by default, automatically rolled back after completion of the test. If a test class is annotated with @Transactional, each test method within that class hierarchy runs within a transaction. Test methods that are not annotated with @Transactional (at the class or method level) are not run within a transaction. Note that @Transactional is not supported on test lifecycle methods — for example, methods annotated with JUnit Jupiter’s @BeforeAll, @BeforeEach, etc. Furthermore, tests that are annotated with @Transactional but have the propagation attribute set to NOT_SUPPORTED or NEVER are not run within a transaction.

그렇다면 어떠한 방식으로 해결할 수 있을까?

@BeforeEach@Transactional 의 대상이므로, @BeforeEach 를 사용하면 되겠다.

하지만, 여전히 찜찜한 것이 모든 테스트에 필요한 것은 아니므로, 굳이 @BeforeEach를 쓰는것보다 각각의 테스트의 상황에 맞게 함수 추가해서 쓰는것이 맞다고 생각된다.

profile
기록하고, 공유합시다

0개의 댓글