통합 테스트코드를 작성중에, @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 thepropagation
attribute set toNOT_SUPPORTED
orNEVER
are not run within a transaction.
그렇다면 어떠한 방식으로 해결할 수 있을까?
@BeforeEach
는 @Transactional
의 대상이므로, @BeforeEach 를 사용하면 되겠다.
하지만, 여전히 찜찜한 것이 모든 테스트에 필요한 것은 아니므로, 굳이 @BeforeEach를 쓰는것보다 각각의 테스트의 상황에 맞게 함수 추가해서 쓰는것이 맞다고 생각된다.